teachers-assistant

[RADIOACTIVE] oh boy did i make bad apps back in the day
git clone git://git.figbert.com/teachers-assistant.git
Log | Files | Refs

commit 94e86d17562f6a48dceac1f0686992aabae112fa
parent 5a1b18f24cb715ff5642f5d62df0ddfec05ffc73
Author: Naomi Welner <naomi@Naomis-MacBook-Air.local>
Date:   Wed, 13 Mar 2019 17:04:49 -0700

Adding existing files

Diffstat:
M.DS_Store | 0
MPodfile | 1+
MPodfile.lock | 17++++++++++++++++-
DPods/.DS_Store | 0
APods/GTMSessionFetcher/LICENSE | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/README.md | 23+++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMGatherInputStream.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMGatherInputStream.m | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMMIMEDocument.h | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMMIMEDocument.m | 631+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.m | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcher.h | 1305+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcher.m | 4579+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.h | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.m | 982+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcherService.h | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionFetcherService.m | 1365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m | 1954+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/LICENSE | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/README.md | 48++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GTLRDefines.h | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheets.h | 13+++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.h | 9245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.m | 2856+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.h | 1078+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.m | 497+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.m | 39+++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.m | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.m | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.m | 373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.h | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.m | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.m | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRObject.h | 317+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRObject.m | 760+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.h | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.m | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.m | 1060+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRService.h | 879+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRService.m | 2883+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.h | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.m | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.h | 29+++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.m | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.h | 34++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.m | 44++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.m | 511+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.m | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPods/Manifest.lock | 17++++++++++++++++-
MPods/Pods.xcodeproj/project.pbxproj | 2100+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
APods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GoogleAPIClientForREST.xcscheme | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/Pods-TeachersAssistant.xcscheme | 4++--
MPods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist | 24+++++++++++++++++++-----
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-Info.plist | 26++++++++++++++++++++++++++
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-dummy.m | 5+++++
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-prefix.pch | 12++++++++++++
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-umbrella.h | 23+++++++++++++++++++++++
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.modulemap | 6++++++
APods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.xcconfig | 9+++++++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-Info.plist | 26++++++++++++++++++++++++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-dummy.m | 5+++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-prefix.pch | 12++++++++++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-umbrella.h | 35+++++++++++++++++++++++++++++++++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap | 6++++++
APods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.xcconfig | 9+++++++++
MPods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.markdown | 412+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.plist | 424+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-frameworks.sh | 4++++
APods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-resources.sh | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.debug.xcconfig | 6+++---
MPods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.release.xcconfig | 6+++---
MPods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.debug.xcconfig | 6+++---
MPods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.release.xcconfig | 6+++---
MTeachers' Assistant/AppDelegate.swift | 2+-
MTeachers' Assistant/GoogleService-Info.plist | 13++++++-------
ATeachers' Assistant/Images/Settings Button.png | 0
MTeachers' Assistant/Info.plist | 15++++++++++++++-
MTeachersAssistant.xcodeproj/project.pbxproj | 14++++++++------
MTeachersAssistant.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist | 2+-
MTeachersAssistant.xcworkspace/xcuserdata/naomi.xcuserdatad/UserInterfaceState.xcuserstate | 0
91 files changed, 38352 insertions(+), 790 deletions(-)

diff --git a/.DS_Store b/.DS_Store Binary files differ. diff --git a/Podfile b/Podfile @@ -5,6 +5,7 @@ target 'TeachersAssistant' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! pod 'Firebase/Core' + pod 'GoogleAPIClientForREST/Sheets' target 'TeachersAssistantTests' do inherit! :search_paths diff --git a/Podfile.lock b/Podfile.lock @@ -19,6 +19,11 @@ PODS: - FirebaseCore (~> 5.2) - GoogleUtilities/Environment (~> 5.2) - GoogleUtilities/UserDefaults (~> 5.2) + - GoogleAPIClientForREST/Core (1.3.8): + - GTMSessionFetcher (>= 1.1.7) + - GoogleAPIClientForREST/Sheets (1.3.8): + - GoogleAPIClientForREST/Core + - GTMSessionFetcher (>= 1.1.7) - GoogleAppMeasurement (5.7.0): - GoogleUtilities/AppDelegateSwizzler (~> 5.2) - GoogleUtilities/MethodSwizzler (~> 5.2) @@ -43,6 +48,11 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (5.3.7): - GoogleUtilities/Logger + - GTMSessionFetcher (1.2.1): + - GTMSessionFetcher/Full (= 1.2.1) + - GTMSessionFetcher/Core (1.2.1) + - GTMSessionFetcher/Full (1.2.1): + - GTMSessionFetcher/Core (= 1.2.1) - nanopb (0.3.901): - nanopb/decode (= 0.3.901) - nanopb/encode (= 0.3.901) @@ -51,6 +61,7 @@ PODS: DEPENDENCIES: - Firebase/Core + - GoogleAPIClientForREST/Sheets SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -58,8 +69,10 @@ SPEC REPOS: - FirebaseAnalytics - FirebaseCore - FirebaseInstanceID + - GoogleAPIClientForREST - GoogleAppMeasurement - GoogleUtilities + - GTMSessionFetcher - nanopb SPEC CHECKSUMS: @@ -67,10 +80,12 @@ SPEC CHECKSUMS: FirebaseAnalytics: 23851fe602c872130a2c5c55040b302120346cc2 FirebaseCore: 52f851b30e11360f1e67cf04b1edfebf0a47a2d3 FirebaseInstanceID: bd6fc5a258884e206fd5c474ebe4f5b00e21770e + GoogleAPIClientForREST: 5447a194eae517986cafe6421a5330b80b820591 GoogleAppMeasurement: 6cf307834da065863f9faf4c0de0a936d81dd832 GoogleUtilities: 111a012f4c3a29c9e7c954c082fafd6ee3c999c0 + GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 -PODFILE CHECKSUM: 28902fd401dcd2075e3dd4d30d9e0c95e065ff7b +PODFILE CHECKSUM: 4852c02225a8d343844dd5ce2a4039dcb525ad3c COCOAPODS: 1.6.1 diff --git a/Pods/.DS_Store b/Pods/.DS_Store Binary files differ. diff --git a/Pods/GTMSessionFetcher/LICENSE b/Pods/GTMSessionFetcher/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Pods/GTMSessionFetcher/README.md b/Pods/GTMSessionFetcher/README.md @@ -0,0 +1,23 @@ +# Google Toolbox for Mac - Session Fetcher # + +**Project site** <https://github.com/google/gtm-session-fetcher><br> +**Discussion group** <http://groups.google.com/group/google-toolbox-for-mac> + +[![Build Status](https://travis-ci.org/google/gtm-session-fetcher.svg?branch=master)](https://travis-ci.org/google/gtm-session-fetcher) + +`GTMSessionFetcher` makes it easy for Cocoa applications to perform http +operations. The fetcher is implemented as a wrapper on `NSURLSession`, so its +behavior is asynchronous and uses operating-system settings on iOS and Mac OS X. + +Features include: +- Simple to build; only one source/header file pair is required +- Simple to use: takes just two lines of code to fetch a request +- Supports upload and download sessions +- Flexible cookie storage +- Automatic retry on errors, with exponential backoff +- Support for generating multipart MIME upload streams +- Easy, convenient logging of http requests and responses +- Supports plug-in authentication such as with GTMAppAuth +- Easily testable; self-mocking +- Automatic rate limiting when created by the `GTMSessionFetcherService` factory class +- Fully independent of other projects diff --git a/Pods/GTMSessionFetcher/Source/GTMGatherInputStream.h b/Pods/GTMSessionFetcher/Source/GTMGatherInputStream.h @@ -0,0 +1,52 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +// The GTMGatherInput stream is an input stream implementation that is to be +// instantiated with an NSArray of NSData objects. It works in the traditional +// scatter/gather vector I/O model. Rather than allocating a big NSData object +// to hold all of the data and performing a copy into that object, the +// GTMGatherInputStream will maintain a reference to the NSArray and read from +// each NSData in turn as the read method is called. You should not alter the +// underlying set of NSData objects until all read operations on this input +// stream have completed. + +#import <Foundation/Foundation.h> + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + +// Avoid multiple declaration of this class. +// +// Note: This should match the declaration of GTMGatherInputStream in GTMMIMEDocument.m + +#ifndef GTM_GATHERINPUTSTREAM_DECLARED +#define GTM_GATHERINPUTSTREAM_DECLARED + +@interface GTMGatherInputStream : NSInputStream <NSStreamDelegate> + ++ (NSInputStream *)streamWithArray:(NSArray *)dataArray GTM_NONNULL((1)); + +@end + +#endif // GTM_GATHERINPUTSTREAM_DECLARED diff --git a/Pods/GTMSessionFetcher/Source/GTMGatherInputStream.m b/Pods/GTMSessionFetcher/Source/GTMGatherInputStream.m @@ -0,0 +1,185 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMGatherInputStream.h" + +@implementation GTMGatherInputStream { + NSArray *_dataArray; // NSDatas that should be "gathered" and streamed. + NSUInteger _arrayIndex; // Index in the array of the current NSData. + long long _dataOffset; // Offset in the current NSData we are processing. + NSStreamStatus _streamStatus; + id<NSStreamDelegate> __weak _delegate; // Stream delegate, defaults to self. +} + ++ (NSInputStream *)streamWithArray:(NSArray *)dataArray { + return [(GTMGatherInputStream *)[self alloc] initWithArray:dataArray]; +} + +- (instancetype)initWithArray:(NSArray *)dataArray { + self = [super init]; + if (self) { + _dataArray = dataArray; + _delegate = self; // An NSStream's default delegate should be self. + } + return self; +} + +#pragma mark - NSStream + +- (void)open { + _arrayIndex = 0; + _dataOffset = 0; + _streamStatus = NSStreamStatusOpen; +} + +- (void)close { + _streamStatus = NSStreamStatusClosed; +} + +- (id<NSStreamDelegate>)delegate { + return _delegate; +} + +- (void)setDelegate:(id<NSStreamDelegate>)delegate { + if (delegate == nil) { + _delegate = self; + } else { + _delegate = delegate; + } +} + +- (id)propertyForKey:(NSString *)key { + if ([key isEqual:NSStreamFileCurrentOffsetKey]) { + return @([self absoluteOffset]); + } + return nil; +} + +- (BOOL)setProperty:(id)property forKey:(NSString *)key { + if ([key isEqual:NSStreamFileCurrentOffsetKey]) { + NSNumber *absoluteOffsetNumber = property; + [self setAbsoluteOffset:absoluteOffsetNumber.longLongValue]; + return YES; + } + return NO; +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { +} + +- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { +} + +- (NSStreamStatus)streamStatus { + return _streamStatus; +} + +- (NSError *)streamError { + return nil; +} + +#pragma mark - NSInputStream + +- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len { + NSInteger bytesRead = 0; + NSUInteger bytesRemaining = len; + + // Read bytes from the currently-indexed array. + while ((bytesRemaining > 0) && (_arrayIndex < _dataArray.count)) { + NSData *data = [_dataArray objectAtIndex:_arrayIndex]; + + NSUInteger dataLen = data.length; + NSUInteger dataBytesLeft = dataLen - (NSUInteger)_dataOffset; + + NSUInteger bytesToCopy = MIN(bytesRemaining, dataBytesLeft); + NSRange range = NSMakeRange((NSUInteger) _dataOffset, bytesToCopy); + + [data getBytes:(buffer + bytesRead) range:range]; + + bytesRead += bytesToCopy; + _dataOffset += bytesToCopy; + bytesRemaining -= bytesToCopy; + + if (_dataOffset == (long long)dataLen) { + _dataOffset = 0; + _arrayIndex++; + } + } + if (_arrayIndex >= _dataArray.count) { + _streamStatus = NSStreamStatusAtEnd; + } + return bytesRead; +} + +- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len { + return NO; // We don't support this style of reading. +} + +- (BOOL)hasBytesAvailable { + // If we return no, the read never finishes, even if we've already delivered all the bytes. + return YES; +} + +#pragma mark - NSStreamDelegate + +- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { + id<NSStreamDelegate> delegate = _delegate; + if (delegate != self) { + [delegate stream:self handleEvent:streamEvent]; + } +} + +#pragma mark - Private + +- (long long)absoluteOffset { + long long absoluteOffset = 0; + NSUInteger index = 0; + for (NSData *data in _dataArray) { + if (index >= _arrayIndex) { + break; + } + absoluteOffset += data.length; + ++index; + } + absoluteOffset += _dataOffset; + return absoluteOffset; +} + +- (void)setAbsoluteOffset:(long long)absoluteOffset { + if (absoluteOffset < 0) { + absoluteOffset = 0; + } + _arrayIndex = 0; + _dataOffset = absoluteOffset; + for (NSData *data in _dataArray) { + long long dataLen = (long long) data.length; + if (dataLen > _dataOffset) { + break; + } + _arrayIndex++; + _dataOffset -= dataLen; + } + if (_arrayIndex == _dataArray.count) { + if (_dataOffset > 0) { + _dataOffset = 0; + } + } +} + +@end diff --git a/Pods/GTMSessionFetcher/Source/GTMMIMEDocument.h b/Pods/GTMSessionFetcher/Source/GTMMIMEDocument.h @@ -0,0 +1,148 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +// This is a simple class to create or parse a MIME document. + +// To create a MIME document, allocate a new GTMMIMEDocument and start adding parts. +// When you are done adding parts, call generateInputStream or generateDispatchData. +// +// A good reference for MIME is http://en.wikipedia.org/wiki/MIME + +#import <Foundation/Foundation.h> + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + +#ifndef GTM_DECLARE_GENERICS + #if __has_feature(objc_generics) + #define GTM_DECLARE_GENERICS 1 + #else + #define GTM_DECLARE_GENERICS 0 + #endif +#endif + +#ifndef GTM_NSArrayOf + #if GTM_DECLARE_GENERICS + #define GTM_NSArrayOf(value) NSArray<value> + #define GTM_NSDictionaryOf(key, value) NSDictionary<key, value> + #else + #define GTM_NSArrayOf(value) NSArray + #define GTM_NSDictionaryOf(key, value) NSDictionary + #endif // GTM_DECLARE_GENERICS +#endif // GTM_NSArrayOf + + +// GTMMIMEDocumentPart represents a part of a MIME document. +// +// +[GTMMIMEDocument MIMEPartsWithBoundary:data:] returns an array of these. +@interface GTMMIMEDocumentPart : NSObject + +@property(nonatomic, readonly) GTM_NSDictionaryOf(NSString *, NSString *) *headers; +@property(nonatomic, readonly) NSData *headerData; +@property(nonatomic, readonly) NSData *body; +@property(nonatomic, readonly) NSUInteger length; + ++ (instancetype)partWithHeaders:(NSDictionary *)headers body:(NSData *)body; + +@end + +@interface GTMMIMEDocument : NSObject + +// Get or set the unique boundary for the parts that have been added. +// +// When creating a MIME document from parts, this is typically calculated +// automatically after all parts have been added. +@property(nonatomic, copy) NSString *boundary; + +#pragma mark - Methods for Creating a MIME Document + ++ (instancetype)MIMEDocument; + +// Adds a new part to this mime document with the given headers and body. +// The headers keys and values should be NSStrings. +// Adding a part may cause the boundary string to change. +- (void)addPartWithHeaders:(GTM_NSDictionaryOf(NSString *, NSString *) *)headers + body:(NSData *)body GTM_NONNULL((1,2)); + +// An inputstream that can be used to efficiently read the contents of the MIME document. +// +// Any parameter may be null if the result is not wanted. +- (void)generateInputStream:(NSInputStream **)outStream + length:(unsigned long long *)outLength + boundary:(NSString **)outBoundary; + +// A dispatch_data_t with the contents of the MIME document. +// +// Note: dispatch_data_t is one-way toll-free bridged so the result +// may be cast directly to NSData *. +// +// Any parameter may be null if the result is not wanted. +- (void)generateDispatchData:(dispatch_data_t *)outDispatchData + length:(unsigned long long *)outLength + boundary:(NSString **)outBoundary; + +// Utility method for making a header section, including trailing newlines. ++ (NSData *)dataWithHeaders:(GTM_NSDictionaryOf(NSString *, NSString *) *)headers; + +#pragma mark - Methods for Parsing a MIME Document + +// Method for parsing out an array of MIME parts from a MIME document. +// +// Returns an array of GTMMIMEDocumentParts. Returns nil if no part can +// be found. ++ (GTM_NSArrayOf(GTMMIMEDocumentPart *) *)MIMEPartsWithBoundary:(NSString *)boundary + data:(NSData *)fullDocumentData; + +// Utility method for efficiently searching possibly discontiguous NSData +// for occurrences of target byte. This method does not "flatten" an NSData +// that is composed of discontiguous blocks. +// +// The byte offsets of non-overlapping occurrences of the target are returned as +// NSNumbers in the array. ++ (void)searchData:(NSData *)data + targetBytes:(const void *)targetBytes + targetLength:(NSUInteger)targetLength + foundOffsets:(GTM_NSArrayOf(NSNumber *) **)outFoundOffsets; + +// Utility method to parse header bytes into an NSDictionary. ++ (GTM_NSDictionaryOf(NSString *, NSString *) *)headersWithData:(NSData *)data; + +// ------ UNIT TESTING ONLY BELOW ------ + +// Internal methods, exposed for unit testing only. +- (void)seedRandomWith:(u_int32_t)seed; + ++ (NSUInteger)findBytesWithNeedle:(const unsigned char *)needle + needleLength:(NSUInteger)needleLength + haystack:(const unsigned char *)haystack + haystackLength:(NSUInteger)haystackLength + foundOffset:(NSUInteger *)foundOffset; + ++ (void)searchData:(NSData *)data + targetBytes:(const void *)targetBytes + targetLength:(NSUInteger)targetLength + foundOffsets:(GTM_NSArrayOf(NSNumber *) **)outFoundOffsets + foundBlockNumbers:(GTM_NSArrayOf(NSNumber *) **)outFoundBlockNumbers; + +@end diff --git a/Pods/GTMSessionFetcher/Source/GTMMIMEDocument.m b/Pods/GTMSessionFetcher/Source/GTMMIMEDocument.m @@ -0,0 +1,631 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMMIMEDocument.h" + +// Avoid a hard dependency on GTMGatherInputStream. +#ifndef GTM_GATHERINPUTSTREAM_DECLARED +#define GTM_GATHERINPUTSTREAM_DECLARED + +@interface GTMGatherInputStream : NSInputStream <NSStreamDelegate> + ++ (NSInputStream *)streamWithArray:(NSArray *)dataArray GTM_NONNULL((1)); + +@end +#endif // GTM_GATHERINPUTSTREAM_DECLARED + +// FindBytes +// +// Helper routine to search for the existence of a set of bytes (needle) within +// a presumed larger set of bytes (haystack). Can find the first part of the +// needle at the very end of the haystack. +// +// Returns the needle length on complete success, the number of bytes matched +// if a partial needle was found at the end of the haystack, and 0 on failure. +static NSUInteger FindBytes(const unsigned char *needle, NSUInteger needleLen, + const unsigned char *haystack, NSUInteger haystackLen, + NSUInteger *foundOffset); + +// SearchDataForBytes +// +// This implements the functionality of the +searchData: methods below. See the documentation +// for those methods. +static void SearchDataForBytes(NSData *data, const void *targetBytes, NSUInteger targetLength, + NSMutableArray *foundOffsets, NSMutableArray *foundBlockNumbers); + +@implementation GTMMIMEDocumentPart { + NSDictionary *_headers; + NSData *_headerData; // Header content including the ending "\r\n". + NSData *_bodyData; +} + +@synthesize headers = _headers, + headerData = _headerData, + body = _bodyData; + +@dynamic length; + ++ (instancetype)partWithHeaders:(NSDictionary *)headers body:(NSData *)body { + return [[self alloc] initWithHeaders:headers body:body]; +} + +- (instancetype)initWithHeaders:(NSDictionary *)headers body:(NSData *)body { + self = [super init]; + if (self) { + _bodyData = body; + _headers = headers; + } + return self; +} + +// Returns true if the part's header or data contain the given set of bytes. +// +// NOTE: We assume that the 'bytes' we are checking for do not contain "\r\n", +// so we don't need to check the concatenation of the header and body bytes. +- (BOOL)containsBytes:(const unsigned char *)bytes length:(NSUInteger)length { + // This uses custom search code rather than strcpy because the encoded data may contain + // null values. + NSData *headerData = self.headerData; + return (FindBytes(bytes, length, headerData.bytes, headerData.length, NULL) == length || + FindBytes(bytes, length, _bodyData.bytes, _bodyData.length, NULL) == length); +} + +- (NSData *)headerData { + if (!_headerData) { + _headerData = [GTMMIMEDocument dataWithHeaders:_headers]; + } + return _headerData; +} + +- (NSData *)body { + return _bodyData; +} + +- (NSUInteger)length { + return _headerData.length + _bodyData.length; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p (headers %lu keys, body %lu bytes)", + [self class], self, (unsigned long)_headers.count, + (unsigned long)_bodyData.length]; +} + +- (BOOL)isEqual:(GTMMIMEDocumentPart *)other { + if (self == other) return YES; + if (![other isKindOfClass:[GTMMIMEDocumentPart class]]) return NO; + return ((_bodyData == other->_bodyData || [_bodyData isEqual:other->_bodyData]) + && (_headers == other->_headers || [_headers isEqual:other->_headers])); +} + +- (NSUInteger)hash { + return _bodyData.hash | _headers.hash; +} + +@end + +@implementation GTMMIMEDocument { + NSMutableArray *_parts; // Ordered array of GTMMIMEDocumentParts. + unsigned long long _length; // Length in bytes of the document. + NSString *_boundary; + u_int32_t _randomSeed; // For testing. +} + ++ (instancetype)MIMEDocument { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _parts = [[NSMutableArray alloc] init]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p (%lu parts)", + [self class], self, (unsigned long)_parts.count]; +} + +#pragma mark - Joining Parts + +// Adds a new part to this mime document with the given headers and body. +- (void)addPartWithHeaders:(NSDictionary *)headers body:(NSData *)body { + GTMMIMEDocumentPart *part = [GTMMIMEDocumentPart partWithHeaders:headers body:body]; + [_parts addObject:part]; + _boundary = nil; +} + +// For unit testing only, seeds the random number generator so that we will +// have reproducible boundary strings. +- (void)seedRandomWith:(u_int32_t)seed { + _randomSeed = seed; + _boundary = nil; +} + +- (u_int32_t)random { + if (_randomSeed) { + // For testing only. + return _randomSeed++; + } else { + return arc4random(); + } +} + +// Computes the mime boundary to use. This should only be called +// after all the desired document parts have been added since it must compute +// a boundary that does not exist in the document data. +- (NSString *)boundary { + if (_boundary) { + return _boundary; + } + + // Use an easily-readable boundary string. + NSString *const kBaseBoundary = @"END_OF_PART"; + + _boundary = kBaseBoundary; + + // If the boundary isn't unique, append random numbers, up to 10 attempts; + // if that's still not unique, use a random number sequence instead, and call it good. + BOOL didCollide = NO; + + const int maxTries = 10; // Arbitrarily chosen maximum attempts. + for (int tries = 0; tries < maxTries; ++tries) { + + NSData *data = [_boundary dataUsingEncoding:NSUTF8StringEncoding]; + const void *dataBytes = data.bytes; + NSUInteger dataLen = data.length; + + for (GTMMIMEDocumentPart *part in _parts) { + didCollide = [part containsBytes:dataBytes length:dataLen]; + if (didCollide) break; + } + + if (!didCollide) break; // We're fine, no more attempts needed. + + // Try again with a random number appended. + _boundary = [NSString stringWithFormat:@"%@_%08x", kBaseBoundary, [self random]]; + } + + if (didCollide) { + // Fallback... two random numbers. + _boundary = [NSString stringWithFormat:@"%08x_tedborg_%08x", [self random], [self random]]; + } + return _boundary; +} + +- (void)setBoundary:(NSString *)str { + _boundary = [str copy]; +} + +// Internal method. +- (void)generateDataArray:(NSMutableArray *)dataArray + length:(unsigned long long *)outLength + boundary:(NSString **)outBoundary { + + // The input stream is of the form: + // --boundary + // [part_1_headers] + // [part_1_data] + // --boundary + // [part_2_headers] + // [part_2_data] + // --boundary-- + + // First we set up our boundary NSData objects. + NSString *boundary = self.boundary; + + NSString *mainBoundary = [NSString stringWithFormat:@"\r\n--%@\r\n", boundary]; + NSString *endBoundary = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary]; + + NSData *mainBoundaryData = [mainBoundary dataUsingEncoding:NSUTF8StringEncoding]; + NSData *endBoundaryData = [endBoundary dataUsingEncoding:NSUTF8StringEncoding]; + + // Now we add them all in proper order to our dataArray. + unsigned long long length = 0; + + for (GTMMIMEDocumentPart *part in _parts) { + [dataArray addObject:mainBoundaryData]; + [dataArray addObject:part.headerData]; + [dataArray addObject:part.body]; + + length += part.length + mainBoundaryData.length; + } + + [dataArray addObject:endBoundaryData]; + length += endBoundaryData.length; + + if (outLength) *outLength = length; + if (outBoundary) *outBoundary = boundary; +} + +- (void)generateInputStream:(NSInputStream **)outStream + length:(unsigned long long *)outLength + boundary:(NSString **)outBoundary { + NSMutableArray *dataArray = outStream ? [NSMutableArray array] : nil; + [self generateDataArray:dataArray + length:outLength + boundary:outBoundary]; + + if (outStream) { + Class streamClass = NSClassFromString(@"GTMGatherInputStream"); + NSAssert(streamClass != nil, @"GTMGatherInputStream not available."); + + *outStream = [streamClass streamWithArray:dataArray]; + } +} + +- (void)generateDispatchData:(dispatch_data_t *)outDispatchData + length:(unsigned long long *)outLength + boundary:(NSString **)outBoundary { + NSMutableArray *dataArray = outDispatchData ? [NSMutableArray array] : nil; + [self generateDataArray:dataArray + length:outLength + boundary:outBoundary]; + + if (outDispatchData) { + // Create an empty data accumulator. + dispatch_data_t dataAccumulator; + + dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + for (NSData *partData in dataArray) { + __block NSData *immutablePartData = [partData copy]; + dispatch_data_t newDataPart = + dispatch_data_create(immutablePartData.bytes, immutablePartData.length, bgQueue, ^{ + // We want the data retained until this block executes. + immutablePartData = nil; + }); + + if (dataAccumulator == nil) { + // First part. + dataAccumulator = newDataPart; + } else { + // Append the additional part. + dataAccumulator = dispatch_data_create_concat(dataAccumulator, newDataPart); + } + } + *outDispatchData = dataAccumulator; + } +} + ++ (NSData *)dataWithHeaders:(NSDictionary *)headers { + // Generate the header data by coalescing the dictionary as lines of "key: value\r\n". + NSMutableString* headerString = [NSMutableString string]; + + // Sort the header keys so we have a deterministic order for unit testing. + SEL sortSel = @selector(caseInsensitiveCompare:); + NSArray *sortedKeys = [headers.allKeys sortedArrayUsingSelector:sortSel]; + + for (NSString *key in sortedKeys) { + NSString *value = [headers objectForKey:key]; + +#if DEBUG + // Look for troublesome characters in the header keys & values. + NSCharacterSet *badKeyChars = [NSCharacterSet characterSetWithCharactersInString:@":\r\n"]; + NSCharacterSet *badValueChars = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"]; + + NSRange badRange = [key rangeOfCharacterFromSet:badKeyChars]; + NSAssert(badRange.location == NSNotFound, @"invalid key: %@", key); + + badRange = [value rangeOfCharacterFromSet:badValueChars]; + NSAssert(badRange.location == NSNotFound, @"invalid value: %@", value); +#endif + + [headerString appendFormat:@"%@: %@\r\n", key, value]; + } + // Headers end with an extra blank line. + [headerString appendString:@"\r\n"]; + + NSData *result = [headerString dataUsingEncoding:NSUTF8StringEncoding]; + return result; +} + +#pragma mark - Separating Parts + ++ (NSArray *)MIMEPartsWithBoundary:(NSString *)boundary + data:(NSData *)fullDocumentData { + // In MIME documents, the boundary is preceded by CRLF and two dashes, and followed + // at the end by two dashes. + NSData *boundaryData = [boundary dataUsingEncoding:NSUTF8StringEncoding]; + NSUInteger boundaryLength = boundaryData.length; + + NSMutableArray *foundBoundaryOffsets; + [self searchData:fullDocumentData + targetBytes:boundaryData.bytes + targetLength:boundaryLength + foundOffsets:&foundBoundaryOffsets]; + + // According to rfc1341, ignore anything before the first boundary, or after the last, though two + // dashes are expected to follow the last boundary. + if (foundBoundaryOffsets.count < 2) { + return nil; + } + + // Wrap the full document data with a dispatch_data_t for more efficient slicing + // and dicing. + dispatch_data_t dataWrapper; + if ([fullDocumentData conformsToProtocol:@protocol(OS_dispatch_data)]) { + dataWrapper = (dispatch_data_t)fullDocumentData; + } else { + // A no-op self invocation on fullDocumentData will keep it retained until the block is invoked. + dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dataWrapper = dispatch_data_create(fullDocumentData.bytes, + fullDocumentData.length, + bgQueue, ^{ [fullDocumentData self]; }); + } + NSMutableArray *parts; + NSInteger previousBoundaryOffset = -1; + NSInteger partCounter = -1; + NSInteger numberOfPartsWithHeaders = 0; + for (NSNumber *currentBoundaryOffset in foundBoundaryOffsets) { + ++partCounter; + if (previousBoundaryOffset == -1) { + // This is the first boundary. + previousBoundaryOffset = currentBoundaryOffset.integerValue; + continue; + } else { + // Create a part data subrange between the previous boundary and this one. + // + // The last four bytes before a boundary are CRLF--. + // The first two bytes following a boundary are either CRLF or, for the last boundary, --. + NSInteger previousPartDataStartOffset = + previousBoundaryOffset + (NSInteger)boundaryLength + 2; + NSInteger previousPartDataEndOffset = currentBoundaryOffset.integerValue - 4; + NSInteger previousPartDataLength = previousPartDataEndOffset - previousPartDataStartOffset; + + if (previousPartDataLength < 2) { + // The preceding part was too short to be useful. +#if DEBUG + NSLog(@"MIME part %ld has %ld bytes", (long)partCounter - 1, + (long)previousPartDataLength); +#endif + } else { + if (!parts) parts = [NSMutableArray array]; + + dispatch_data_t partData = + dispatch_data_create_subrange(dataWrapper, + (size_t)previousPartDataStartOffset, (size_t)previousPartDataLength); + // Scan the part data for the separator between headers and body. After the CRLF, + // either the headers start immediately, or there's another CRLF and there are no headers. + // + // We need to map the part data to get the first two bytes. (Or we could cast it to + // NSData and get the bytes pointer of that.) If we're concerned that a single part + // data may be expensive to map, we could make a subrange here for just the first two bytes, + // and map that two-byte subrange. + const void *partDataBuffer; + size_t partDataBufferSize; + dispatch_data_t mappedPartData NS_VALID_UNTIL_END_OF_SCOPE = + dispatch_data_create_map(partData, &partDataBuffer, &partDataBufferSize); + dispatch_data_t bodyData; + NSDictionary *headers; + BOOL hasAnotherCRLF = (((char *)partDataBuffer)[0] == '\r' + && ((char *)partDataBuffer)[1] == '\n'); + mappedPartData = nil; + + if (hasAnotherCRLF) { + // There are no headers; skip the CRLF to get to the body, and leave headers nil. + bodyData = dispatch_data_create_subrange(partData, 2, (size_t)previousPartDataLength - 2); + } else { + // There are part headers. They are separated from body data by CRLFCRLF. + NSArray *crlfOffsets; + [self searchData:(NSData *)partData + targetBytes:"\r\n\r\n" + targetLength:4 + foundOffsets:&crlfOffsets]; + if (crlfOffsets.count == 0) { +#if DEBUG + // We could not distinguish body and headers. + NSLog(@"MIME part %ld lacks a header separator: %@", (long)partCounter - 1, + [[NSString alloc] initWithData:(NSData *)partData encoding:NSUTF8StringEncoding]); +#endif + } else { + NSInteger headerSeparatorOffset = ((NSNumber *)crlfOffsets.firstObject).integerValue; + dispatch_data_t headerData = + dispatch_data_create_subrange(partData, 0, (size_t)headerSeparatorOffset); + headers = [self headersWithData:(NSData *)headerData]; + + bodyData = dispatch_data_create_subrange(partData, (size_t)headerSeparatorOffset + 4, + (size_t)(previousPartDataLength - (headerSeparatorOffset + 4))); + + numberOfPartsWithHeaders++; + } // crlfOffsets.count == 0 + } // hasAnotherCRLF + GTMMIMEDocumentPart *part = [GTMMIMEDocumentPart partWithHeaders:headers + body:(NSData *)bodyData]; + [parts addObject:part]; + } // previousPartDataLength < 2 + previousBoundaryOffset = currentBoundaryOffset.integerValue; + } + } +#if DEBUG + // In debug builds, warn if a reasonably long document lacks any CRLF characters. + if (numberOfPartsWithHeaders == 0) { + NSUInteger length = fullDocumentData.length; + if (length > 20) { // Reasonably long. + NSMutableArray *foundCRLFs; + [self searchData:fullDocumentData + targetBytes:"\r\n" + targetLength:2 + foundOffsets:&foundCRLFs]; + if (foundCRLFs.count == 0) { + // Parts were logged above (due to lacking header separators.) + NSLog(@"Warning: MIME document lacks any headers (may have wrong line endings)"); + } + } + } +#endif // DEBUG + return parts; +} + +// Efficiently search the supplied data for the target bytes. +// +// This uses enumerateByteRangesUsingBlock: to scan for bytes. It can find +// the target even if it spans multiple separate byte ranges. +// +// Returns an array of found byte offset values, as NSNumbers. ++ (void)searchData:(NSData *)data + targetBytes:(const void *)targetBytes + targetLength:(NSUInteger)targetLength + foundOffsets:(GTM_NSArrayOf(NSNumber *) **)outFoundOffsets { + NSMutableArray *foundOffsets = [NSMutableArray array]; + SearchDataForBytes(data, targetBytes, targetLength, foundOffsets, NULL); + *outFoundOffsets = foundOffsets; +} + + +// This version of searchData: also returns the block numbers (0-based) where the +// target was found, used for testing that the supplied dispatch_data buffer +// has not been flattened. ++ (void)searchData:(NSData *)data + targetBytes:(const void *)targetBytes + targetLength:(NSUInteger)targetLength + foundOffsets:(GTM_NSArrayOf(NSNumber *) **)outFoundOffsets + foundBlockNumbers:(GTM_NSArrayOf(NSNumber *) **)outFoundBlockNumbers { + NSMutableArray *foundOffsets = [NSMutableArray array]; + NSMutableArray *foundBlockNumbers = [NSMutableArray array]; + + SearchDataForBytes(data, targetBytes, targetLength, foundOffsets, foundBlockNumbers); + *outFoundOffsets = foundOffsets; + *outFoundBlockNumbers = foundBlockNumbers; +} + +static void SearchDataForBytes(NSData *data, const void *targetBytes, NSUInteger targetLength, + NSMutableArray *foundOffsets, NSMutableArray *foundBlockNumbers) { + __block NSUInteger priorPartialMatchAmount = 0; + __block NSInteger priorPartialMatchStartingBlockNumber = -1; + __block NSInteger blockNumber = -1; + + [data enumerateByteRangesUsingBlock:^(const void *bytes, + NSRange byteRange, + BOOL *stop) { + // Search for the first character in the current range. + const void *ptr = bytes; + NSInteger remainingInCurrentRange = (NSInteger)byteRange.length; + ++blockNumber; + + if (priorPartialMatchAmount > 0) { + NSUInteger amountRemainingToBeMatched = targetLength - priorPartialMatchAmount; + NSUInteger remainingFoundOffset; + NSUInteger amountMatched = FindBytes(targetBytes + priorPartialMatchAmount, + amountRemainingToBeMatched, + ptr, (NSUInteger)remainingInCurrentRange, &remainingFoundOffset); + if (amountMatched == 0 || remainingFoundOffset > 0) { + // No match of the rest of the prior partial match in this range. + } else if (amountMatched < amountRemainingToBeMatched) { + // Another partial match; we're done with this range. + priorPartialMatchAmount = priorPartialMatchAmount + amountMatched; + return; + } else { + // The offset is in an earlier range. + NSUInteger offset = byteRange.location - priorPartialMatchAmount; + [foundOffsets addObject:@(offset)]; + [foundBlockNumbers addObject:@(priorPartialMatchStartingBlockNumber)]; + priorPartialMatchStartingBlockNumber = -1; + } + priorPartialMatchAmount = 0; + } + + while (remainingInCurrentRange > 0) { + NSUInteger offsetFromPtr; + NSUInteger amountMatched = FindBytes(targetBytes, targetLength, ptr, + (NSUInteger)remainingInCurrentRange, &offsetFromPtr); + if (amountMatched == 0) { + // No match in this range. + return; + } + if (amountMatched < targetLength) { + // Found a partial target. If there's another range, we'll check for the rest. + priorPartialMatchAmount = amountMatched; + priorPartialMatchStartingBlockNumber = blockNumber; + return; + } + // Found the full target. + NSUInteger globalOffset = byteRange.location + (NSUInteger)(ptr - bytes) + offsetFromPtr; + + [foundOffsets addObject:@(globalOffset)]; + [foundBlockNumbers addObject:@(blockNumber)]; + + ptr += targetLength + offsetFromPtr; + remainingInCurrentRange -= (targetLength + offsetFromPtr); + } + }]; +} + +// Internal method only for testing; this calls through the static method. ++ (NSUInteger)findBytesWithNeedle:(const unsigned char *)needle + needleLength:(NSUInteger)needleLength + haystack:(const unsigned char *)haystack + haystackLength:(NSUInteger)haystackLength + foundOffset:(NSUInteger *)foundOffset { + return FindBytes(needle, needleLength, haystack, haystackLength, foundOffset); +} + +// Utility method to parse header bytes into an NSDictionary. ++ (NSDictionary *)headersWithData:(NSData *)data { + NSString *headersString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (!headersString) return nil; + + NSMutableDictionary *headers = [NSMutableDictionary dictionary]; + NSScanner *scanner = [NSScanner scannerWithString:headersString]; + // The scanner is skipping leading whitespace and newline characters by default. + NSCharacterSet *newlineCharacters = [NSCharacterSet newlineCharacterSet]; + NSString *key; + NSString *value; + while ([scanner scanUpToString:@":" intoString:&key] + && [scanner scanString:@":" intoString:NULL] + && [scanner scanUpToCharactersFromSet:newlineCharacters intoString:&value]) { + [headers setObject:value forKey:key]; + // Discard the trailing newline. + [scanner scanCharactersFromSet:newlineCharacters intoString:NULL]; + } + return headers; +} + +@end + +// Return how much of the needle was found in the haystack. +// +// If the result is less than needleLen, then the beginning of the needle +// was found at the end of the haystack. +static NSUInteger FindBytes(const unsigned char* needle, NSUInteger needleLen, + const unsigned char* haystack, NSUInteger haystackLen, + NSUInteger *foundOffset) { + const unsigned char *ptr = haystack; + NSInteger remain = (NSInteger)haystackLen; + // Assume memchr is an efficient way to find a match for the first + // byte of the needle, and memcmp is an efficient way to compare a + // range of bytes. + while (remain > 0 && (ptr = memchr(ptr, needle[0], (size_t)remain)) != 0) { + // The first character is present. + NSUInteger offset = (NSUInteger)(ptr - haystack); + remain = (NSInteger)(haystackLen - offset); + + NSUInteger amountToCompare = MIN((NSUInteger)remain, needleLen); + if (memcmp(ptr, needle, amountToCompare) == 0) { + if (foundOffset) *foundOffset = offset; + return amountToCompare; + } + ptr++; + remain--; + } + if (foundOffset) *foundOffset = 0; + return 0; +} diff --git a/Pods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.h b/Pods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.h @@ -0,0 +1,49 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + + +@interface GTMReadMonitorInputStream : NSInputStream <NSStreamDelegate> + ++ (instancetype)inputStreamWithStream:(NSInputStream *)input GTM_NONNULL((1)); + +- (instancetype)initWithStream:(NSInputStream *)input GTM_NONNULL((1)); + +// The read monitor selector is called when bytes have been read. It should have this signature: +// +// - (void)inputStream:(GTMReadMonitorInputStream *)stream +// readIntoBuffer:(uint8_t *)buffer +// length:(int64_t)length; + +@property(atomic, weak) id readDelegate; +@property(atomic, assign) SEL readSelector; + +// Modes for invoking callbacks, when necessary. +@property(atomic, strong) NSArray *runLoopModes; + +@end diff --git a/Pods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.m b/Pods/GTMSessionFetcher/Source/GTMReadMonitorInputStream.m @@ -0,0 +1,190 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMReadMonitorInputStream.h" + +@implementation GTMReadMonitorInputStream { + NSInputStream *_inputStream; // Encapsulated stream that does the work. + + NSThread *_thread; // Thread in which this object was created. + NSArray *_runLoopModes; // Modes for calling callbacks, when necessary. +} + + +@synthesize readDelegate = _readDelegate; +@synthesize readSelector = _readSelector; +@synthesize runLoopModes = _runLoopModes; + +// We'll forward all unhandled messages to the NSInputStream class or to the encapsulated input +// stream. This is needed for all messages sent to NSInputStream which aren't handled by our +// superclass; that includes various private run loop calls. ++ (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { + return [NSInputStream methodSignatureForSelector:selector]; +} + ++ (void)forwardInvocation:(NSInvocation*)invocation { + [invocation invokeWithTarget:[NSInputStream class]]; +} + +- (BOOL)respondsToSelector:(SEL)selector { + return [_inputStream respondsToSelector:selector]; +} + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { + return [_inputStream methodSignatureForSelector:selector]; +} + +- (void)forwardInvocation:(NSInvocation*)invocation { + [invocation invokeWithTarget:_inputStream]; +} + +#pragma mark - + ++ (instancetype)inputStreamWithStream:(NSInputStream *)input { + return [[self alloc] initWithStream:input]; +} + +- (instancetype)initWithStream:(NSInputStream *)input { + self = [super init]; + if (self) { + _inputStream = input; + _thread = [NSThread currentThread]; + } + return self; +} + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +#pragma mark - + +- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len { + // Read from the encapsulated stream. + NSInteger numRead = [_inputStream read:buffer maxLength:len]; + if (numRead > 0) { + if (_readDelegate && _readSelector) { + // Call the read selector with the buffer and number of bytes actually read into it. + BOOL isOnOriginalThread = [_thread isEqual:[NSThread currentThread]]; + if (isOnOriginalThread) { + // Invoke immediately. + NSData *data = [NSData dataWithBytesNoCopy:buffer + length:(NSUInteger)numRead + freeWhenDone:NO]; + [self invokeReadSelectorWithBuffer:data]; + } else { + // Copy the buffer into an NSData to be retained by the performSelector, + // and invoke on the proper thread. + SEL sel = @selector(invokeReadSelectorWithBuffer:); + NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numRead]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if (_runLoopModes) { + [self performSelector:sel + onThread:_thread + withObject:data + waitUntilDone:NO + modes:_runLoopModes]; + } else { + [self performSelector:sel + onThread:_thread + withObject:data + waitUntilDone:NO]; + } +#pragma clang diagnostic pop + } + } + } + return numRead; +} + +- (void)invokeReadSelectorWithBuffer:(NSData *)data { + const void *buffer = data.bytes; + int64_t length = (int64_t)data.length; + + id argSelf = self; + id readDelegate = _readDelegate; + if (readDelegate) { + NSMethodSignature *signature = [readDelegate methodSignatureForSelector:_readSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:_readSelector]; + [invocation setTarget:readDelegate]; + [invocation setArgument:&argSelf atIndex:2]; + [invocation setArgument:&buffer atIndex:3]; + [invocation setArgument:&length atIndex:4]; + [invocation invoke]; + } +} + +- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len { + return [_inputStream getBuffer:buffer length:len]; +} + +- (BOOL)hasBytesAvailable { + return [_inputStream hasBytesAvailable]; +} + +#pragma mark Standard messages + +// Pass expected messages to our encapsulated stream. +// +// We want our encapsulated NSInputStream to handle the standard messages; +// we don't want the superclass to handle them. +- (void)open { + [_inputStream open]; +} + +- (void)close { + [_inputStream close]; +} + +- (id)delegate { + return [_inputStream delegate]; +} + +- (void)setDelegate:(id)delegate { + [_inputStream setDelegate:delegate]; +} + +- (id)propertyForKey:(NSString *)key { + return [_inputStream propertyForKey:key]; +} + +- (BOOL)setProperty:(id)property forKey:(NSString *)key { + return [_inputStream setProperty:property forKey:key]; +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { + [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; +} + +- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { + [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; +} + +- (NSStreamStatus)streamStatus { + return [_inputStream streamStatus]; +} + +- (NSError *)streamError { + return [_inputStream streamError]; +} + +@end diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcher.h b/Pods/GTMSessionFetcher/Source/GTMSessionFetcher.h @@ -0,0 +1,1305 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +// GTMSessionFetcher is a wrapper around NSURLSession for http operations. +// +// What does this offer on top of of NSURLSession? +// +// - Block-style callbacks for useful functionality like progress rather +// than delegate methods. +// - Out-of-process uploads and downloads using NSURLSession, including +// management of fetches after relaunch. +// - Integration with GTMAppAuth for invisible management and refresh of +// authorization tokens. +// - Pretty-printed http logging. +// - Cookies handling that does not interfere with or get interfered with +// by WebKit cookies or on Mac by Safari and other apps. +// - Credentials handling for the http operation. +// - Rate-limiting and cookie grouping when fetchers are created with +// GTMSessionFetcherService. +// +// If the bodyData or bodyFileURL property is set, then a POST request is assumed. +// +// Each fetcher is assumed to be for a one-shot fetch request; don't reuse the object +// for a second fetch. +// +// The fetcher will be self-retained as long as a connection is pending. +// +// To keep user activity private, URLs must have an https scheme (unless the property +// allowedInsecureSchemes is set to permit the scheme.) +// +// Callbacks will be released when the fetch completes or is stopped, so there is no need +// to use weak self references in the callback blocks. +// +// Sample usage: +// +// _fetcherService = [[GTMSessionFetcherService alloc] init]; +// +// GTMSessionFetcher *myFetcher = [_fetcherService fetcherWithURLString:myURLString]; +// myFetcher.retryEnabled = YES; +// myFetcher.comment = @"First profile image"; +// +// // Optionally specify a file URL or NSData for the request body to upload. +// myFetcher.bodyData = [postString dataUsingEncoding:NSUTF8StringEncoding]; +// +// [myFetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { +// if (error != nil) { +// // Server status code or network error. +// // +// // If the domain is kGTMSessionFetcherStatusDomain then the error code +// // is a failure status from the server. +// } else { +// // Fetch succeeded. +// } +// }]; +// +// There is also a beginFetch call that takes a pointer and selector for the completion handler; +// a pointer and selector is a better style when the callback is a substantial, separate method. +// +// NOTE: Fetches may retrieve data from the server even though the server +// returned an error, so the criteria for success is a non-nil error. +// The completion handler is called when the server status is >= 300 with an NSError +// having domain kGTMSessionFetcherStatusDomain and code set to the server status. +// +// Status codes are at <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html> +// +// +// Background session support: +// +// Out-of-process uploads and downloads may be created by setting the fetcher's +// useBackgroundSession property. Data to be uploaded should be provided via +// the uploadFileURL property; the download destination should be specified with +// the destinationFileURL. NOTE: Background upload files should be in a location +// that will be valid even after the device is restarted, so the file should not +// be uploaded from a system temporary or cache directory. +// +// Background session transfers are slower, and should typically be used only +// for very large downloads or uploads (hundreds of megabytes). +// +// When background sessions are used in iOS apps, the application delegate must +// pass through the parameters from UIApplicationDelegate's +// application:handleEventsForBackgroundURLSession:completionHandler: to the +// fetcher class. +// +// When the application has been relaunched, it may also create a new fetcher +// instance to handle completion of the transfers. +// +// - (void)application:(UIApplication *)application +// handleEventsForBackgroundURLSession:(NSString *)identifier +// completionHandler:(void (^)())completionHandler { +// // Application was re-launched on completing an out-of-process download. +// +// // Pass the URLSession info related to this re-launch to the fetcher class. +// [GTMSessionFetcher application:application +// handleEventsForBackgroundURLSession:identifier +// completionHandler:completionHandler]; +// +// // Get a fetcher related to this re-launch and re-hook up a completionHandler to it. +// GTMSessionFetcher *fetcher = [GTMSessionFetcher fetcherWithSessionIdentifier:identifier]; +// NSURL *destinationFileURL = fetcher.destinationFileURL; +// fetcher.completionHandler = ^(NSData *data, NSError *error) { +// [self downloadCompletedToFile:destinationFileURL error:error]; +// }; +// } +// +// +// Threading and queue support: +// +// Networking always happens on a background thread; there is no advantage to +// changing thread or queue to create or start a fetcher. +// +// Callbacks are run on the main thread; alternatively, the app may set the +// fetcher's callbackQueue to a dispatch queue. +// +// Once the fetcher's beginFetch method has been called, the fetcher's methods and +// properties may be accessed from any thread. +// +// Downloading to disk: +// +// To have downloaded data saved directly to disk, specify a file URL for the +// destinationFileURL property. +// +// HTTP methods and headers: +// +// Alternative HTTP methods, like PUT, and custom headers can be specified by +// creating the fetcher with an appropriate NSMutableURLRequest. +// +// +// Caching: +// +// The fetcher avoids caching. That is best for API requests, but may hurt +// repeat fetches of static data. Apps may enable a persistent disk cache by +// customizing the config: +// +// fetcher.configurationBlock = ^(GTMSessionFetcher *configFetcher, +// NSURLSessionConfiguration *config) { +// config.URLCache = [NSURLCache sharedURLCache]; +// }; +// +// Or use the standard system config to share cookie storage with web views +// and to enable disk caching: +// +// fetcher.configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +// +// +// Cookies: +// +// There are three supported mechanisms for remembering cookies between fetches. +// +// By default, a standalone GTMSessionFetcher uses a mutable array held +// statically to track cookies for all instantiated fetchers. This avoids +// cookies being set by servers for the application from interfering with +// Safari and WebKit cookie settings, and vice versa. +// The fetcher cookies are lost when the application quits. +// +// To rely instead on WebKit's global NSHTTPCookieStorage, set the fetcher's +// cookieStorage property: +// myFetcher.cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; +// +// To share cookies with other apps, use the method introduced in iOS 9/OS X 10.11: +// myFetcher.cookieStorage = +// [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:kMyCompanyContainedID]; +// +// To ignore existing cookies and only have cookies related to the single fetch +// be applied, make a temporary cookie storage object: +// myFetcher.cookieStorage = [[GTMSessionCookieStorage alloc] init]; +// +// Note: cookies set while following redirects will be sent to the server, as +// the redirects are followed by the fetcher. +// +// To completely disable cookies, similar to setting cookieStorageMethod to +// kGTMHTTPFetcherCookieStorageMethodNone, adjust the session configuration +// appropriately in the fetcher or fetcher service: +// fetcher.configurationBlock = ^(GTMSessionFetcher *configFetcher, +// NSURLSessionConfiguration *config) { +// config.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever; +// config.HTTPShouldSetCookies = NO; +// }; +// +// If the fetcher is created from a GTMSessionFetcherService object +// then the cookie storage mechanism is set to use the cookie storage in the +// service object rather than the static storage. Disabling cookies in the +// session configuration set on a service object will disable cookies for all +// fetchers created from that GTMSessionFetcherService object, since the session +// configuration is propagated to the fetcher. +// +// +// Monitoring data transfers. +// +// The fetcher supports a variety of properties for progress monitoring +// progress with callback blocks. +// GTMSessionFetcherSendProgressBlock sendProgressBlock +// GTMSessionFetcherReceivedProgressBlock receivedProgressBlock +// GTMSessionFetcherDownloadProgressBlock downloadProgressBlock +// +// If supplied by the server, the anticipated total download size is available +// as [[myFetcher response] expectedContentLength] (and may be -1 for unknown +// download sizes.) +// +// +// Automatic retrying of fetches +// +// The fetcher can optionally create a timer and reattempt certain kinds of +// fetch failures (status codes 408, request timeout; 502, gateway failure; +// 503, service unavailable; 504, gateway timeout; networking errors +// NSURLErrorTimedOut and NSURLErrorNetworkConnectionLost.) The user may +// set a retry selector to customize the type of errors which will be retried. +// +// Retries are done in an exponential-backoff fashion (that is, after 1 second, +// 2, 4, 8, and so on.) +// +// Enabling automatic retries looks like this: +// myFetcher.retryEnabled = YES; +// +// With retries enabled, the completion callbacks are called only +// when no more retries will be attempted. Calling the fetcher's stopFetching +// method will terminate the retry timer, without the finished or failure +// selectors being invoked. +// +// Optionally, the client may set the maximum retry interval: +// myFetcher.maxRetryInterval = 60.0; // in seconds; default is 60 seconds +// // for downloads, 600 for uploads +// +// Servers should never send a 400 or 500 status for errors that are retryable +// by clients, as those values indicate permanent failures. In nearly all +// cases, the default standard retry behavior is correct for clients, and no +// custom client retry behavior is needed or appropriate. Servers that send +// non-retryable status codes and expect the client to retry the request are +// faulty. +// +// Still, the client may provide a block to determine if a status code or other +// error should be retried. The block returns YES to set the retry timer or NO +// to fail without additional fetch attempts. +// +// The retry method may return the |suggestedWillRetry| argument to get the +// default retry behavior. Server status codes are present in the +// error argument, and have the domain kGTMSessionFetcherStatusDomain. The +// user's method may look something like this: +// +// myFetcher.retryBlock = ^(BOOL suggestedWillRetry, NSError *error, +// GTMSessionFetcherRetryResponse response) { +// // Perhaps examine error.domain and error.code, or fetcher.retryCount +// // +// // Respond with YES to start the retry timer, NO to proceed to the failure +// // callback, or suggestedWillRetry to get default behavior for the +// // current error domain and code values. +// response(suggestedWillRetry); +// }; + + +#import <Foundation/Foundation.h> + +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif +#if TARGET_OS_WATCH +#import <WatchKit/WatchKit.h> +#endif + +// By default it is stripped from non DEBUG builds. Developers can override +// this in their project settings. +#ifndef STRIP_GTM_FETCH_LOGGING + #if !DEBUG + #define STRIP_GTM_FETCH_LOGGING 1 + #else + #define STRIP_GTM_FETCH_LOGGING 0 + #endif +#endif + +// Logs in debug builds. +#ifndef GTMSESSION_LOG_DEBUG + #if DEBUG + #define GTMSESSION_LOG_DEBUG(...) NSLog(__VA_ARGS__) + #else + #define GTMSESSION_LOG_DEBUG(...) do { } while (0) + #endif +#endif + +// Asserts in debug builds (or logs in debug builds if GTMSESSION_ASSERT_AS_LOG +// or NS_BLOCK_ASSERTIONS are defined.) +#ifndef GTMSESSION_ASSERT_DEBUG + #if DEBUG && !defined(NS_BLOCK_ASSERTIONS) && !GTMSESSION_ASSERT_AS_LOG + #undef GTMSESSION_ASSERT_AS_LOG + #define GTMSESSION_ASSERT_AS_LOG 1 + #endif + + #if DEBUG && !GTMSESSION_ASSERT_AS_LOG + #define GTMSESSION_ASSERT_DEBUG(...) NSAssert(__VA_ARGS__) + #elif DEBUG + #define GTMSESSION_ASSERT_DEBUG(pred, ...) if (!(pred)) { NSLog(__VA_ARGS__); } + #else + #define GTMSESSION_ASSERT_DEBUG(pred, ...) do { } while (0) + #endif +#endif + +// Asserts in debug builds, logs in release builds (or logs in debug builds if +// GTMSESSION_ASSERT_AS_LOG is defined.) +#ifndef GTMSESSION_ASSERT_DEBUG_OR_LOG + #if DEBUG && !GTMSESSION_ASSERT_AS_LOG + #define GTMSESSION_ASSERT_DEBUG_OR_LOG(...) NSAssert(__VA_ARGS__) + #else + #define GTMSESSION_ASSERT_DEBUG_OR_LOG(pred, ...) if (!(pred)) { NSLog(__VA_ARGS__); } + #endif +#endif + +// Macro useful for examining messages from NSURLSession during debugging. +#if 0 +#define GTM_LOG_SESSION_DELEGATE(...) GTMSESSION_LOG_DEBUG(__VA_ARGS__) +#else +#define GTM_LOG_SESSION_DELEGATE(...) +#endif + +#ifndef GTM_NULLABLE + #if __has_feature(nullability) // Available starting in Xcode 6.3 + #define GTM_NULLABLE_TYPE __nullable + #define GTM_NONNULL_TYPE __nonnull + #define GTM_NULLABLE nullable + #define GTM_NONNULL_DECL nonnull // GTM_NONNULL is used by GTMDefines.h + #define GTM_NULL_RESETTABLE null_resettable + + #define GTM_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN + #define GTM_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END + #else + #define GTM_NULLABLE_TYPE + #define GTM_NONNULL_TYPE + #define GTM_NULLABLE + #define GTM_NONNULL_DECL + #define GTM_NULL_RESETTABLE + #define GTM_ASSUME_NONNULL_BEGIN + #define GTM_ASSUME_NONNULL_END + #endif // __has_feature(nullability) +#endif // GTM_NULLABLE + +#if (TARGET_OS_TV \ + || TARGET_OS_WATCH \ + || (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)) +#define GTMSESSION_DEPRECATE_ON_2016_SDKS(_MSG) __attribute__((deprecated("" _MSG))) +#else +#define GTMSESSION_DEPRECATE_ON_2016_SDKS(_MSG) +#endif + +#ifndef GTM_DECLARE_GENERICS + #if __has_feature(objc_generics) + #define GTM_DECLARE_GENERICS 1 + #else + #define GTM_DECLARE_GENERICS 0 + #endif +#endif + +#ifndef GTM_NSArrayOf + #if GTM_DECLARE_GENERICS + #define GTM_NSArrayOf(value) NSArray<value> + #define GTM_NSDictionaryOf(key, value) NSDictionary<key, value> + #else + #define GTM_NSArrayOf(value) NSArray + #define GTM_NSDictionaryOf(key, value) NSDictionary + #endif // __has_feature(objc_generics) +#endif // GTM_NSArrayOf + +// For iOS, the fetcher can declare itself a background task to allow fetches +// to finish when the app leaves the foreground. +// +// (This is unrelated to providing a background configuration, which allows +// out-of-process uploads and downloads.) +// +// To disallow use of background tasks during fetches, the target should define +// GTM_BACKGROUND_TASK_FETCHING to 0, or alternatively may set the +// skipBackgroundTask property to YES. +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !defined(GTM_BACKGROUND_TASK_FETCHING) + #define GTM_BACKGROUND_TASK_FETCHING 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if (TARGET_OS_TV \ + || TARGET_OS_WATCH \ + || (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0)) + #ifndef GTM_USE_SESSION_FETCHER + #define GTM_USE_SESSION_FETCHER 1 + #endif +#endif + +#if !defined(GTMBridgeFetcher) + // These bridge macros should be identical in GTMHTTPFetcher.h and GTMSessionFetcher.h + #if GTM_USE_SESSION_FETCHER + // Macros to new fetcher class. + #define GTMBridgeFetcher GTMSessionFetcher + #define GTMBridgeFetcherService GTMSessionFetcherService + #define GTMBridgeFetcherServiceProtocol GTMSessionFetcherServiceProtocol + #define GTMBridgeAssertValidSelector GTMSessionFetcherAssertValidSelector + #define GTMBridgeCookieStorage GTMSessionCookieStorage + #define GTMBridgeCleanedUserAgentString GTMFetcherCleanedUserAgentString + #define GTMBridgeSystemVersionString GTMFetcherSystemVersionString + #define GTMBridgeApplicationIdentifier GTMFetcherApplicationIdentifier + #define kGTMBridgeFetcherStatusDomain kGTMSessionFetcherStatusDomain + #define kGTMBridgeFetcherStatusBadRequest GTMSessionFetcherStatusBadRequest + #else + // Macros to old fetcher class. + #define GTMBridgeFetcher GTMHTTPFetcher + #define GTMBridgeFetcherService GTMHTTPFetcherService + #define GTMBridgeFetcherServiceProtocol GTMHTTPFetcherServiceProtocol + #define GTMBridgeAssertValidSelector GTMAssertSelectorNilOrImplementedWithArgs + #define GTMBridgeCookieStorage GTMCookieStorage + #define GTMBridgeCleanedUserAgentString GTMCleanedUserAgentString + #define GTMBridgeSystemVersionString GTMSystemVersionString + #define GTMBridgeApplicationIdentifier GTMApplicationIdentifier + #define kGTMBridgeFetcherStatusDomain kGTMHTTPFetcherStatusDomain + #define kGTMBridgeFetcherStatusBadRequest kGTMHTTPFetcherStatusBadRequest + #endif // GTM_USE_SESSION_FETCHER +#endif + +GTM_ASSUME_NONNULL_BEGIN + +// Notifications +// +// Fetch started and stopped, and fetch retry delay started and stopped. +extern NSString *const kGTMSessionFetcherStartedNotification; +extern NSString *const kGTMSessionFetcherStoppedNotification; +extern NSString *const kGTMSessionFetcherRetryDelayStartedNotification; +extern NSString *const kGTMSessionFetcherRetryDelayStoppedNotification; + +// Completion handler notification. This is intended for use by code capturing +// and replaying fetch requests and results for testing. For fetches where +// destinationFileURL or accumulateDataBlock is set for the fetcher, the data +// will be nil for successful fetches. +// +// This notification is posted on the main thread. +extern NSString *const kGTMSessionFetcherCompletionInvokedNotification; +extern NSString *const kGTMSessionFetcherCompletionDataKey; +extern NSString *const kGTMSessionFetcherCompletionErrorKey; + +// Constants for NSErrors created by the fetcher (excluding server status errors, +// and error objects originating in the OS.) +extern NSString *const kGTMSessionFetcherErrorDomain; + +// The fetcher turns server error status values (3XX, 4XX, 5XX) into NSErrors +// with domain kGTMSessionFetcherStatusDomain. +// +// Any server response body data accompanying the status error is added to the +// userInfo dictionary with key kGTMSessionFetcherStatusDataKey. +extern NSString *const kGTMSessionFetcherStatusDomain; +extern NSString *const kGTMSessionFetcherStatusDataKey; +extern NSString *const kGTMSessionFetcherStatusDataContentTypeKey; + +// When a fetch fails with an error, these keys are included in the error userInfo +// dictionary if retries were attempted. +extern NSString *const kGTMSessionFetcherNumberOfRetriesDoneKey; +extern NSString *const kGTMSessionFetcherElapsedIntervalWithRetriesKey; + +// Background session support requires access to NSUserDefaults. +// If [NSUserDefaults standardUserDefaults] doesn't yield the correct NSUserDefaults for your usage, +// ie for an App Extension, then implement this class/method to return the correct NSUserDefaults. +// https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW6 +@interface GTMSessionFetcherUserDefaultsFactory : NSObject + ++ (NSUserDefaults *)fetcherUserDefaults; + +@end + +#ifdef __cplusplus +} +#endif + +typedef NS_ENUM(NSInteger, GTMSessionFetcherError) { + GTMSessionFetcherErrorDownloadFailed = -1, + GTMSessionFetcherErrorUploadChunkUnavailable = -2, + GTMSessionFetcherErrorBackgroundExpiration = -3, + GTMSessionFetcherErrorBackgroundFetchFailed = -4, + GTMSessionFetcherErrorInsecureRequest = -5, + GTMSessionFetcherErrorTaskCreationFailed = -6, +}; + +typedef NS_ENUM(NSInteger, GTMSessionFetcherStatus) { + // Standard http status codes. + GTMSessionFetcherStatusNotModified = 304, + GTMSessionFetcherStatusBadRequest = 400, + GTMSessionFetcherStatusUnauthorized = 401, + GTMSessionFetcherStatusForbidden = 403, + GTMSessionFetcherStatusPreconditionFailed = 412 +}; + +#ifdef __cplusplus +extern "C" { +#endif + +@class GTMSessionCookieStorage; +@class GTMSessionFetcher; + +// The configuration block is for modifying the NSURLSessionConfiguration only. +// DO NOT change any fetcher properties in the configuration block. +typedef void (^GTMSessionFetcherConfigurationBlock)(GTMSessionFetcher *fetcher, + NSURLSessionConfiguration *configuration); +typedef void (^GTMSessionFetcherSystemCompletionHandler)(void); +typedef void (^GTMSessionFetcherCompletionHandler)(NSData * GTM_NULLABLE_TYPE data, + NSError * GTM_NULLABLE_TYPE error); +typedef void (^GTMSessionFetcherBodyStreamProviderResponse)(NSInputStream *bodyStream); +typedef void (^GTMSessionFetcherBodyStreamProvider)(GTMSessionFetcherBodyStreamProviderResponse response); +typedef void (^GTMSessionFetcherDidReceiveResponseDispositionBlock)(NSURLSessionResponseDisposition disposition); +typedef void (^GTMSessionFetcherDidReceiveResponseBlock)(NSURLResponse *response, + GTMSessionFetcherDidReceiveResponseDispositionBlock dispositionBlock); +typedef void (^GTMSessionFetcherChallengeDispositionBlock)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * GTM_NULLABLE_TYPE credential); +typedef void (^GTMSessionFetcherChallengeBlock)(GTMSessionFetcher *fetcher, + NSURLAuthenticationChallenge *challenge, + GTMSessionFetcherChallengeDispositionBlock dispositionBlock); +typedef void (^GTMSessionFetcherWillRedirectResponse)(NSURLRequest * GTM_NULLABLE_TYPE redirectedRequest); +typedef void (^GTMSessionFetcherWillRedirectBlock)(NSHTTPURLResponse *redirectResponse, + NSURLRequest *redirectRequest, + GTMSessionFetcherWillRedirectResponse response); +typedef void (^GTMSessionFetcherAccumulateDataBlock)(NSData * GTM_NULLABLE_TYPE buffer); +typedef void (^GTMSessionFetcherSimulateByteTransferBlock)(NSData * GTM_NULLABLE_TYPE buffer, + int64_t bytesWritten, + int64_t totalBytesWritten, + int64_t totalBytesExpectedToWrite); +typedef void (^GTMSessionFetcherReceivedProgressBlock)(int64_t bytesWritten, + int64_t totalBytesWritten); +typedef void (^GTMSessionFetcherDownloadProgressBlock)(int64_t bytesWritten, + int64_t totalBytesWritten, + int64_t totalBytesExpectedToWrite); +typedef void (^GTMSessionFetcherSendProgressBlock)(int64_t bytesSent, + int64_t totalBytesSent, + int64_t totalBytesExpectedToSend); +typedef void (^GTMSessionFetcherWillCacheURLResponseResponse)(NSCachedURLResponse * GTM_NULLABLE_TYPE cachedResponse); +typedef void (^GTMSessionFetcherWillCacheURLResponseBlock)(NSCachedURLResponse *proposedResponse, + GTMSessionFetcherWillCacheURLResponseResponse responseBlock); +typedef void (^GTMSessionFetcherRetryResponse)(BOOL shouldRetry); +typedef void (^GTMSessionFetcherRetryBlock)(BOOL suggestedWillRetry, + NSError * GTM_NULLABLE_TYPE error, + GTMSessionFetcherRetryResponse response); + +typedef void (^GTMSessionFetcherTestResponse)(NSHTTPURLResponse * GTM_NULLABLE_TYPE response, + NSData * GTM_NULLABLE_TYPE data, + NSError * GTM_NULLABLE_TYPE error); +typedef void (^GTMSessionFetcherTestBlock)(GTMSessionFetcher *fetcherToTest, + GTMSessionFetcherTestResponse testResponse); + +void GTMSessionFetcherAssertValidSelector(id GTM_NULLABLE_TYPE obj, SEL GTM_NULLABLE_TYPE sel, ...); + +// Utility functions for applications self-identifying to servers via a +// user-agent header + +// The "standard" user agent includes the application identifier, taken from the bundle, +// followed by a space and the system version string. Pass nil to use +mainBundle as the source +// of the bundle identifier. +// +// Applications may use this as a starting point for their own user agent strings, perhaps +// with additional sections appended. Use GTMFetcherCleanedUserAgentString() below to +// clean up any string being added to the user agent. +NSString *GTMFetcherStandardUserAgentString(NSBundle * GTM_NULLABLE_TYPE bundle); + +// Make a generic name and version for the current application, like +// com.example.MyApp/1.2.3 relying on the bundle identifier and the +// CFBundleShortVersionString or CFBundleVersion. +// +// The bundle ID may be overridden as the base identifier string by +// adding to the bundle's Info.plist a "GTMUserAgentID" key. +// +// If no bundle ID or override is available, the process name preceded +// by "proc_" is used. +NSString *GTMFetcherApplicationIdentifier(NSBundle * GTM_NULLABLE_TYPE bundle); + +// Make an identifier like "MacOSX/10.7.1" or "iPod_Touch/4.1 hw/iPod1_1" +NSString *GTMFetcherSystemVersionString(void); + +// Make a parseable user-agent identifier from the given string, replacing whitespace +// and commas with underscores, and removing other characters that may interfere +// with parsing of the full user-agent string. +// +// For example, @"[My App]" would become @"My_App" +NSString *GTMFetcherCleanedUserAgentString(NSString *str); + +// Grab the data from an input stream. Since streams cannot be assumed to be rewindable, +// this may be destructive; the caller can try to rewind the stream (by setting the +// NSStreamFileCurrentOffsetKey property) or can just use the NSData to make a new +// NSInputStream. This function is intended to facilitate testing rather than be used in +// production. +// +// This function operates synchronously on the current thread. Depending on how the +// input stream is implemented, it may be appropriate to dispatch to a different +// queue before calling this function. +// +// Failure is indicated by a returned data value of nil. +NSData * GTM_NULLABLE_TYPE GTMDataFromInputStream(NSInputStream *inputStream, NSError **outError); + +#ifdef __cplusplus +} // extern "C" +#endif + + +#if !GTM_USE_SESSION_FETCHER +@protocol GTMHTTPFetcherServiceProtocol; +#endif + +// This protocol allows abstract references to the fetcher service, primarily for +// fetchers (which may be compiled without the fetcher service class present.) +// +// Apps should not need to use this protocol. +@protocol GTMSessionFetcherServiceProtocol <NSObject> +// This protocol allows us to call into the service without requiring +// GTMSessionFetcherService sources in this project + +@property(atomic, strong) dispatch_queue_t callbackQueue; + +- (BOOL)fetcherShouldBeginFetching:(GTMSessionFetcher *)fetcher; +- (void)fetcherDidCreateSession:(GTMSessionFetcher *)fetcher; +- (void)fetcherDidBeginFetching:(GTMSessionFetcher *)fetcher; +- (void)fetcherDidStop:(GTMSessionFetcher *)fetcher; + +- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request; +- (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher; + +@property(atomic, assign) BOOL reuseSession; +- (GTM_NULLABLE NSURLSession *)session; +- (GTM_NULLABLE NSURLSession *)sessionForFetcherCreation; +- (GTM_NULLABLE id<NSURLSessionDelegate>)sessionDelegate; +- (GTM_NULLABLE NSDate *)stoppedAllFetchersDate; + +// Methods for compatibility with the old GTMHTTPFetcher. +@property(readonly, strong, GTM_NULLABLE) NSOperationQueue *delegateQueue; + +@end // @protocol GTMSessionFetcherServiceProtocol + +#ifndef GTM_FETCHER_AUTHORIZATION_PROTOCOL +#define GTM_FETCHER_AUTHORIZATION_PROTOCOL 1 +@protocol GTMFetcherAuthorizationProtocol <NSObject> +@required +// This protocol allows us to call the authorizer without requiring its sources +// in this project. +- (void)authorizeRequest:(GTM_NULLABLE NSMutableURLRequest *)request + delegate:(id)delegate + didFinishSelector:(SEL)sel; + +- (void)stopAuthorization; + +- (void)stopAuthorizationForRequest:(NSURLRequest *)request; + +- (BOOL)isAuthorizingRequest:(NSURLRequest *)request; + +- (BOOL)isAuthorizedRequest:(NSURLRequest *)request; + +@property(strong, readonly, GTM_NULLABLE) NSString *userEmail; + +@optional + +// Indicate if authorization may be attempted. Even if this succeeds, +// authorization may fail if the user's permissions have been revoked. +@property(readonly) BOOL canAuthorize; + +// For development only, allow authorization of non-SSL requests, allowing +// transmission of the bearer token unencrypted. +@property(assign) BOOL shouldAuthorizeAllRequests; + +- (void)authorizeRequest:(GTM_NULLABLE NSMutableURLRequest *)request + completionHandler:(void (^)(NSError * GTM_NULLABLE_TYPE error))handler; + +#if GTM_USE_SESSION_FETCHER +@property (weak, GTM_NULLABLE) id<GTMSessionFetcherServiceProtocol> fetcherService; +#else +@property (weak, GTM_NULLABLE) id<GTMHTTPFetcherServiceProtocol> fetcherService; +#endif + +- (BOOL)primeForRefresh; + +@end +#endif // GTM_FETCHER_AUTHORIZATION_PROTOCOL + +#if GTM_BACKGROUND_TASK_FETCHING +// A protocol for an alternative target for messages from GTMSessionFetcher to UIApplication. +// Set the target using +[GTMSessionFetcher setSubstituteUIApplication:] +@protocol GTMUIApplicationProtocol <NSObject> +- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithName:(nullable NSString *)taskName + expirationHandler:(void(^ __nullable)(void))handler; +- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier; +@end +#endif + +#pragma mark - + +// GTMSessionFetcher objects are used for async retrieval of an http get or post +// +// See additional comments at the beginning of this file +@interface GTMSessionFetcher : NSObject <NSURLSessionDelegate> + +// Create a fetcher +// +// fetcherWithRequest will return an autoreleased fetcher, but if +// the connection is successfully created, the connection should retain the +// fetcher for the life of the connection as well. So the caller doesn't have +// to retain the fetcher explicitly unless they want to be able to cancel it. ++ (instancetype)fetcherWithRequest:(GTM_NULLABLE NSURLRequest *)request; + +// Convenience methods that make a request, like +fetcherWithRequest ++ (instancetype)fetcherWithURL:(NSURL *)requestURL; ++ (instancetype)fetcherWithURLString:(NSString *)requestURLString; + +// Methods for creating fetchers to continue previous fetches. ++ (instancetype)fetcherWithDownloadResumeData:(NSData *)resumeData; ++ (GTM_NULLABLE instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier; + +// Returns an array of currently active fetchers for background sessions, +// both restarted and newly created ones. ++ (GTM_NSArrayOf(GTMSessionFetcher *) *)fetchersForBackgroundSessions; + +// Designated initializer. +// +// Applications should create fetchers with a "fetcherWith..." method on a fetcher +// service or a class method, not with this initializer. +// +// The configuration should typically be nil. Applications needing to customize +// the configuration may do so by setting the configurationBlock property. +- (instancetype)initWithRequest:(GTM_NULLABLE NSURLRequest *)request + configuration:(GTM_NULLABLE NSURLSessionConfiguration *)configuration; + +// The fetcher's request. This may not be set after beginFetch has been invoked. The request +// may change due to redirects. +@property(strong, GTM_NULLABLE) NSURLRequest *request; + +// Set a header field value on the request. Header field value changes will not +// affect a fetch after the fetch has begun. +- (void)setRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field; + +// Data used for resuming a download task. +@property(atomic, readonly, GTM_NULLABLE) NSData *downloadResumeData; + +// The configuration; this must be set before the fetch begins. If no configuration is +// set or inherited from the fetcher service, then the fetcher uses an ephemeral config. +// +// NOTE: This property should typically be nil. Applications needing to customize +// the configuration should do so by setting the configurationBlock property. +// That allows the fetcher to pick an appropriate base configuration, with the +// application setting only the configuration properties it needs to customize. +@property(atomic, strong, GTM_NULLABLE) NSURLSessionConfiguration *configuration; + +// A block the client may use to customize the configuration used to create the session. +// +// This is called synchronously, either on the thread that begins the fetch or, during a retry, +// on the main thread. The configuration block may be called repeatedly if multiple fetchers are +// created. +// +// The configuration block is for modifying the NSURLSessionConfiguration only. +// DO NOT change any fetcher properties in the configuration block. Fetcher properties +// may be set in the fetcher service prior to fetcher creation, or on the fetcher prior +// to invoking beginFetch. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherConfigurationBlock configurationBlock; + +// A session is created as needed by the fetcher. A fetcher service object +// may maintain sessions for multiple fetches to the same host. +@property(atomic, strong, GTM_NULLABLE) NSURLSession *session; + +// The task in flight. +@property(atomic, readonly, GTM_NULLABLE) NSURLSessionTask *sessionTask; + +// The background session identifier. +@property(atomic, readonly, GTM_NULLABLE) NSString *sessionIdentifier; + +// Indicates a fetcher created to finish a background session task. +@property(atomic, readonly) BOOL wasCreatedFromBackgroundSession; + +// Additional user-supplied data to encode into the session identifier. Since session identifier +// length limits are unspecified, this should be kept small. Key names beginning with an underscore +// are reserved for use by the fetcher. +@property(atomic, strong, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSString *) *sessionUserInfo; + +// The human-readable description to be assigned to the task. +@property(atomic, copy, GTM_NULLABLE) NSString *taskDescription; + +// The priority assigned to the task, if any. Use NSURLSessionTaskPriorityLow, +// NSURLSessionTaskPriorityDefault, or NSURLSessionTaskPriorityHigh. +@property(atomic, assign) float taskPriority; + +// The fetcher encodes information used to resume a session in the session identifier. +// This method, intended for internal use returns the encoded information. The sessionUserInfo +// dictionary is stored as identifier metadata. +- (GTM_NULLABLE GTM_NSDictionaryOf(NSString *, NSString *) *)sessionIdentifierMetadata; + +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +// The app should pass to this method the completion handler passed in the app delegate method +// application:handleEventsForBackgroundURLSession:completionHandler: ++ (void)application:(UIApplication *)application + handleEventsForBackgroundURLSession:(NSString *)identifier + completionHandler:(GTMSessionFetcherSystemCompletionHandler)completionHandler; +#endif + +// Indicate that a newly created session should be a background session. +// A new session identifier will be created by the fetcher. +// +// Warning: The only thing background sessions are for is rare download +// of huge, batched files of data. And even just for those, there's a lot +// of pain and hackery needed to get transfers to actually happen reliably +// with background sessions. +// +// Don't try to upload or download in many background sessions, since the system +// will impose an exponentially increasing time penalty to prevent the app from +// getting too much background execution time. +// +// References: +// +// "Moving to Fewer, Larger Transfers" +// https://forums.developer.apple.com/thread/14853 +// +// "NSURLSession’s Resume Rate Limiter" +// https://forums.developer.apple.com/thread/14854 +// +// "Background Session Task state persistence" +// https://forums.developer.apple.com/thread/11554 +// +@property(assign) BOOL useBackgroundSession; + +// Indicates if the fetcher was started using a background session. +@property(atomic, readonly, getter=isUsingBackgroundSession) BOOL usingBackgroundSession; + +// Indicates if uploads should use an upload task. This is always set for file or stream-provider +// bodies, but may be set explicitly for NSData bodies. +@property(atomic, assign) BOOL useUploadTask; + +// Indicates that the fetcher is using a session that may be shared with other fetchers. +@property(atomic, readonly) BOOL canShareSession; + +// By default, the fetcher allows only secure (https) schemes unless this +// property is set, or the GTM_ALLOW_INSECURE_REQUESTS build flag is set. +// +// For example, during debugging when fetching from a development server that lacks SSL support, +// this may be set to @[ @"http" ], or when the fetcher is used to retrieve local files, +// this may be set to @[ @"file" ]. +// +// This should be left as nil for release builds to avoid creating the opportunity for +// leaking private user behavior and data. If a server is providing insecure URLs +// for fetching by the client app, report the problem as server security & privacy bug. +// +// For builds with the iOS 9/OS X 10.11 and later SDKs, this property is required only when +// the app specifies NSAppTransportSecurity/NSAllowsArbitraryLoads in the main bundle's Info.plist. +@property(atomic, copy, GTM_NULLABLE) GTM_NSArrayOf(NSString *) *allowedInsecureSchemes; + +// By default, the fetcher prohibits localhost requests unless this property is set, +// or the GTM_ALLOW_INSECURE_REQUESTS build flag is set. +// +// For localhost requests, the URL scheme is not checked when this property is set. +// +// For builds with the iOS 9/OS X 10.11 and later SDKs, this property is required only when +// the app specifies NSAppTransportSecurity/NSAllowsArbitraryLoads in the main bundle's Info.plist. +@property(atomic, assign) BOOL allowLocalhostRequest; + +// By default, the fetcher requires valid server certs. This may be bypassed +// temporarily for development against a test server with an invalid cert. +@property(atomic, assign) BOOL allowInvalidServerCertificates; + +// Cookie storage object for this fetcher. If nil, the fetcher will use a static cookie +// storage instance shared among fetchers. If this fetcher was created by a fetcher service +// object, it will be set to use the service object's cookie storage. See Cookies section above for +// the full discussion. +// +// Because as of Jan 2014 standalone instances of NSHTTPCookieStorage do not actually +// store any cookies (Radar 15735276) we use our own subclass, GTMSessionCookieStorage, +// to hold cookies in memory. +@property(atomic, strong, GTM_NULLABLE) NSHTTPCookieStorage *cookieStorage; + +// Setting the credential is optional; it is used if the connection receives +// an authentication challenge. +@property(atomic, strong, GTM_NULLABLE) NSURLCredential *credential; + +// Setting the proxy credential is optional; it is used if the connection +// receives an authentication challenge from a proxy. +@property(atomic, strong, GTM_NULLABLE) NSURLCredential *proxyCredential; + +// If body data, body file URL, or body stream provider is not set, then a GET request +// method is assumed. +@property(atomic, strong, GTM_NULLABLE) NSData *bodyData; + +// File to use as the request body. This forces use of an upload task. +@property(atomic, strong, GTM_NULLABLE) NSURL *bodyFileURL; + +// Length of body to send, expected or actual. +@property(atomic, readonly) int64_t bodyLength; + +// The body stream provider may be called repeatedly to provide a body. +// Setting a body stream provider forces use of an upload task. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherBodyStreamProvider bodyStreamProvider; + +// Object to add authorization to the request, if needed. +// +// This may not be changed once beginFetch has been invoked. +@property(atomic, strong, GTM_NULLABLE) id<GTMFetcherAuthorizationProtocol> authorizer; + +// The service object that created and monitors this fetcher, if any. +@property(atomic, strong) id<GTMSessionFetcherServiceProtocol> service; + +// The host, if any, used to classify this fetcher in the fetcher service. +@property(atomic, copy, GTM_NULLABLE) NSString *serviceHost; + +// The priority, if any, used for starting fetchers in the fetcher service. +// +// Lower values are higher priority; the default is 0, and values may +// be negative or positive. This priority affects only the start order of +// fetchers that are being delayed by a fetcher service when the running fetchers +// exceeds the service's maxRunningFetchersPerHost. A priority of NSIntegerMin will +// exempt this fetcher from delay. +@property(atomic, assign) NSInteger servicePriority; + +// The delegate's optional didReceiveResponse block may be used to inspect or alter +// the session task response. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherDidReceiveResponseBlock didReceiveResponseBlock; + +// The delegate's optional challenge block may be used to inspect or alter +// the session task challenge. +// +// If this block is not set, the fetcher's default behavior for the NSURLSessionTask +// didReceiveChallenge: delegate method is to use the fetcher's respondToChallenge: method +// which relies on the fetcher's credential and proxyCredential properties. +// +// Warning: This may be called repeatedly if the challenge fails. Check +// challenge.previousFailureCount to identify repeated invocations. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherChallengeBlock challengeBlock; + +// The delegate's optional willRedirect block may be used to inspect or alter +// the redirection. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherWillRedirectBlock willRedirectBlock; + +// The optional send progress block reports body bytes uploaded. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherSendProgressBlock sendProgressBlock; + +// The optional accumulate block may be set by clients wishing to accumulate data +// themselves rather than let the fetcher append each buffer to an NSData. +// +// When this is called with nil data (such as on redirect) the client +// should empty its accumulation buffer. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherAccumulateDataBlock accumulateDataBlock; + +// The optional received progress block may be used to monitor data +// received from a data task. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherReceivedProgressBlock receivedProgressBlock; + +// The delegate's optional downloadProgress block may be used to monitor download +// progress in writing to disk. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherDownloadProgressBlock downloadProgressBlock; + +// The delegate's optional willCacheURLResponse block may be used to alter the cached +// NSURLResponse. The user may prevent caching by passing nil to the block's response. +// +// This is called on the callback queue. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherWillCacheURLResponseBlock willCacheURLResponseBlock; + +// Enable retrying; see comments at the top of this file. Setting +// retryEnabled=YES resets the min and max retry intervals. +@property(atomic, assign, getter=isRetryEnabled) BOOL retryEnabled; + +// Retry block is optional for retries. +// +// If present, this block should call the response block with YES to cause a retry or NO to end the +// fetch. +// See comments at the top of this file. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherRetryBlock retryBlock; + +// Retry intervals must be strictly less than maxRetryInterval, else +// they will be limited to maxRetryInterval and no further retries will +// be attempted. Setting maxRetryInterval to 0.0 will reset it to the +// default value, 60 seconds for downloads and 600 seconds for uploads. +@property(atomic, assign) NSTimeInterval maxRetryInterval; + +// Starting retry interval. Setting minRetryInterval to 0.0 will reset it +// to a random value between 1.0 and 2.0 seconds. Clients should normally not +// set this except for unit testing. +@property(atomic, assign) NSTimeInterval minRetryInterval; + +// Multiplier used to increase the interval between retries, typically 2.0. +// Clients should not need to set this. +@property(atomic, assign) double retryFactor; + +// Number of retries attempted. +@property(atomic, readonly) NSUInteger retryCount; + +// Interval delay to precede next retry. +@property(atomic, readonly) NSTimeInterval nextRetryInterval; + +#if GTM_BACKGROUND_TASK_FETCHING +// Skip use of a UIBackgroundTask, thus requiring fetches to complete when the app is in the +// foreground. +// +// Targets should define GTM_BACKGROUND_TASK_FETCHING to 0 to avoid use of a UIBackgroundTask +// on iOS to allow fetches to complete in the background. This property is available when +// it's not practical to set the preprocessor define. +@property(atomic, assign) BOOL skipBackgroundTask; +#endif // GTM_BACKGROUND_TASK_FETCHING + +// Begin fetching the request +// +// The delegate may optionally implement the callback or pass nil for the selector or handler. +// +// The delegate and all callback blocks are retained between the beginFetch call until after the +// finish callback, or until the fetch is stopped. +// +// An error is passed to the callback for server statuses 300 or +// higher, with the status stored as the error object's code. +// +// finishedSEL has a signature like: +// - (void)fetcher:(GTMSessionFetcher *)fetcher +// finishedWithData:(NSData *)data +// error:(NSError *)error; +// +// If the application has specified a destinationFileURL or an accumulateDataBlock +// for the fetcher, the data parameter passed to the callback will be nil. + +- (void)beginFetchWithDelegate:(GTM_NULLABLE id)delegate + didFinishSelector:(GTM_NULLABLE SEL)finishedSEL; + +- (void)beginFetchWithCompletionHandler:(GTM_NULLABLE GTMSessionFetcherCompletionHandler)handler; + +// Returns YES if this fetcher is in the process of fetching a URL. +@property(atomic, readonly, getter=isFetching) BOOL fetching; + +// Cancel the fetch of the request that's currently in progress. The completion handler +// will not be called. +- (void)stopFetching; + +// A block to be called when the fetch completes. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherCompletionHandler completionHandler; + +// A block to be called if download resume data becomes available. +@property(atomic, strong, GTM_NULLABLE) void (^resumeDataBlock)(NSData *); + +// Return the status code from the server response. +@property(atomic, readonly) NSInteger statusCode; + +// Return the http headers from the response. +@property(atomic, strong, readonly, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSString *) *responseHeaders; + +// The response, once it's been received. +@property(atomic, strong, readonly, GTM_NULLABLE) NSURLResponse *response; + +// Bytes downloaded so far. +@property(atomic, readonly) int64_t downloadedLength; + +// Buffer of currently-downloaded data, if available. +@property(atomic, readonly, strong, GTM_NULLABLE) NSData *downloadedData; + +// Local path to which the downloaded file will be moved. +// +// If a file already exists at the path, it will be overwritten. +// Will create the enclosing folders if they are not present. +@property(atomic, strong, GTM_NULLABLE) NSURL *destinationFileURL; + +// The time this fetcher originally began fetching. This is useful as a time +// barrier for ignoring irrelevant fetch notifications or callbacks. +@property(atomic, strong, readonly, GTM_NULLABLE) NSDate *initialBeginFetchDate; + +// userData is retained solely for the convenience of the client. +@property(atomic, strong, GTM_NULLABLE) id userData; + +// Stored property values are retained solely for the convenience of the client. +@property(atomic, copy, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, id) *properties; + +- (void)setProperty:(GTM_NULLABLE id)obj forKey:(NSString *)key; // Pass nil for obj to remove the property. +- (GTM_NULLABLE id)propertyForKey:(NSString *)key; + +- (void)addPropertiesFromDictionary:(GTM_NSDictionaryOf(NSString *, id) *)dict; + +// Comments are useful for logging, so are strongly recommended for each fetcher. +@property(atomic, copy, GTM_NULLABLE) NSString *comment; + +- (void)setCommentWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); + +// Log of request and response, if logging is enabled +@property(atomic, copy, GTM_NULLABLE) NSString *log; + +// Callbacks are run on this queue. If none is supplied, the main queue is used. +@property(atomic, strong, GTM_NULL_RESETTABLE) dispatch_queue_t callbackQueue; + +// The queue used internally by the session to invoke its delegate methods in the fetcher. +// +// Application callbacks are always called by the fetcher on the callbackQueue above, +// not on this queue. Apps should generally not change this queue. +// +// The default delegate queue is the main queue. +// +// This value is ignored after the session has been created, so this +// property should be set in the fetcher service rather in the fetcher as it applies +// to a shared session. +@property(atomic, strong, GTM_NULL_RESETTABLE) NSOperationQueue *sessionDelegateQueue; + +// Spin the run loop or sleep the thread, discarding events, until the fetch has completed. +// +// This is only for use in testing or in tools without a user interface. +// +// Note: Synchronous fetches should never be used by shipping apps; they are +// sufficient reason for rejection from the app store. +// +// Returns NO if timed out. +- (BOOL)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds; + +// Test block is optional for testing. +// +// If present, this block will cause the fetcher to skip starting the session, and instead +// use the test block response values when calling the completion handler and delegate code. +// +// Test code can set this on the fetcher or on the fetcher service. For testing libraries +// that use a fetcher without exposing either the fetcher or the fetcher service, the global +// method setGlobalTestBlock: will set the block for all fetchers that do not have a test +// block set. +// +// The test code can pass nil for all response parameters to indicate that the fetch +// should proceed. +// +// Applications can exclude test block support by setting GTM_DISABLE_FETCHER_TEST_BLOCK. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherTestBlock testBlock; + ++ (void)setGlobalTestBlock:(GTM_NULLABLE GTMSessionFetcherTestBlock)block; + +// When using the testBlock, |testBlockAccumulateDataChunkCount| is the desired number of chunks to +// divide the response data into if the client has streaming enabled. The data will be divided up to +// |testBlockAccumulateDataChunkCount| chunks; however, the exact amount may vary depending on the +// size of the response data (e.g. a 1-byte response can only be divided into one chunk). +@property(atomic, readwrite) NSUInteger testBlockAccumulateDataChunkCount; + +#if GTM_BACKGROUND_TASK_FETCHING +// For testing or to override UIApplication invocations, apps may specify an alternative +// target for messages to UIApplication. ++ (void)setSubstituteUIApplication:(nullable id<GTMUIApplicationProtocol>)substituteUIApplication; ++ (nullable id<GTMUIApplicationProtocol>)substituteUIApplication; +#endif // GTM_BACKGROUND_TASK_FETCHING + +// Exposed for testing. ++ (GTMSessionCookieStorage *)staticCookieStorage; ++ (BOOL)appAllowsInsecureRequests; + +#if STRIP_GTM_FETCH_LOGGING +// If logging is stripped, provide a stub for the main method +// for controlling logging. ++ (void)setLoggingEnabled:(BOOL)flag; ++ (BOOL)isLoggingEnabled; + +#else + +// These methods let an application log specific body text, such as the text description of a binary +// request or response. The application should set the fetcher to defer response body logging until +// the response has been received and the log response body has been set by the app. For example: +// +// fetcher.logRequestBody = [binaryObject stringDescription]; +// fetcher.deferResponseBodyLogging = YES; +// [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { +// if (error == nil) { +// fetcher.logResponseBody = [[[MyThing alloc] initWithData:data] stringDescription]; +// } +// fetcher.deferResponseBodyLogging = NO; +// }]; + +@property(atomic, copy, GTM_NULLABLE) NSString *logRequestBody; +@property(atomic, assign) BOOL deferResponseBodyLogging; +@property(atomic, copy, GTM_NULLABLE) NSString *logResponseBody; + +// Internal logging support. +@property(atomic, readonly) NSData *loggedStreamData; +@property(atomic, assign) BOOL hasLoggedError; +@property(atomic, strong, GTM_NULLABLE) NSURL *redirectedFromURL; +- (void)appendLoggedStreamData:(NSData *)dataToAdd; +- (void)clearLoggedStreamData; + +#endif // STRIP_GTM_FETCH_LOGGING + +@end + +@interface GTMSessionFetcher (BackwardsCompatibilityOnly) +// Clients using GTMSessionFetcher should set the cookie storage explicitly themselves. +// This method is just for compatibility with the old GTMHTTPFetcher class. +- (void)setCookieStorageMethod:(NSInteger)method; +@end + +// Until we can just instantiate NSHTTPCookieStorage for local use, we'll +// implement all the public methods ourselves. This stores cookies only in +// memory. Additional methods are provided for testing. +// +// iOS 9/OS X 10.11 added +[NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:] +// which may also be used to create cookie storage. +@interface GTMSessionCookieStorage : NSHTTPCookieStorage + +// Add the array off cookies to the storage, replacing duplicates. +// Also removes expired cookies from the storage. +- (void)setCookies:(GTM_NULLABLE GTM_NSArrayOf(NSHTTPCookie *) *)cookies; + +- (void)removeAllCookies; + +@end + +// Macros to monitor synchronization blocks in debug builds. +// These report problems using GTMSessionCheckDebug. +// +// GTMSessionMonitorSynchronized Start monitoring a top-level-only +// @sync scope. +// GTMSessionMonitorRecursiveSynchronized Start monitoring a top-level or +// recursive @sync scope. +// GTMSessionCheckSynchronized Verify that the current execution +// is inside a @sync scope. +// GTMSessionCheckNotSynchronized Verify that the current execution +// is not inside a @sync scope. +// +// Example usage: +// +// - (void)myExternalMethod { +// @synchronized(self) { +// GTMSessionMonitorSynchronized(self) +// +// - (void)myInternalMethod { +// GTMSessionCheckSynchronized(self); +// +// - (void)callMyCallbacks { +// GTMSessionCheckNotSynchronized(self); +// +// GTMSessionCheckNotSynchronized is available for verifying the code isn't +// in a deadlockable @sync state when posting notifications and invoking +// callbacks. Don't use GTMSessionCheckNotSynchronized immediately before a +// @sync scope; the normal recursiveness check of GTMSessionMonitorSynchronized +// can catch those. + +#ifdef __OBJC__ +// If asserts are entirely no-ops, the synchronization monitor is just a bunch +// of counting code that doesn't report exceptional circumstances in any way. +// Only build the synchronization monitor code if NS_BLOCK_ASSERTIONS is not +// defined or asserts are being logged instead. +#if DEBUG && (!defined(NS_BLOCK_ASSERTIONS) || GTMSESSION_ASSERT_AS_LOG) + #define __GTMSessionMonitorSynchronizedVariableInner(varname, counter) \ + varname ## counter + #define __GTMSessionMonitorSynchronizedVariable(varname, counter) \ + __GTMSessionMonitorSynchronizedVariableInner(varname, counter) + + #define GTMSessionMonitorSynchronized(obj) \ + NS_VALID_UNTIL_END_OF_SCOPE id \ + __GTMSessionMonitorSynchronizedVariable(__monitor, __COUNTER__) = \ + [[GTMSessionSyncMonitorInternal alloc] initWithSynchronizationObject:obj \ + allowRecursive:NO \ + functionName:__func__] + + #define GTMSessionMonitorRecursiveSynchronized(obj) \ + NS_VALID_UNTIL_END_OF_SCOPE id \ + __GTMSessionMonitorSynchronizedVariable(__monitor, __COUNTER__) = \ + [[GTMSessionSyncMonitorInternal alloc] initWithSynchronizationObject:obj \ + allowRecursive:YES \ + functionName:__func__] + + #define GTMSessionCheckSynchronized(obj) { \ + GTMSESSION_ASSERT_DEBUG( \ + [GTMSessionSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + @"GTMSessionCheckSynchronized(" #obj ") failed: not sync'd" \ + @" on " #obj " in %s. Call stack:\n%@", \ + __func__, [NSThread callStackSymbols]); \ + } + + #define GTMSessionCheckNotSynchronized(obj) { \ + GTMSESSION_ASSERT_DEBUG( \ + ![GTMSessionSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + @"GTMSessionCheckNotSynchronized(" #obj ") failed: was sync'd" \ + @" on " #obj " in %s by %@. Call stack:\n%@", __func__, \ + [GTMSessionSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + [NSThread callStackSymbols]); \ + } + +// GTMSessionSyncMonitorInternal is a private class that keeps track of the +// beginning and end of synchronized scopes. +// +// This class should not be used directly, but only via the +// GTMSessionMonitorSynchronized macro. +@interface GTMSessionSyncMonitorInternal : NSObject +- (instancetype)initWithSynchronizationObject:(id)object + allowRecursive:(BOOL)allowRecursive + functionName:(const char *)functionName; +// Return the names of the functions that hold sync on the object, or nil if none. ++ (NSArray *)functionsHoldingSynchronizationOnObject:(id)object; +@end + +#else + #define GTMSessionMonitorSynchronized(obj) do { } while (0) + #define GTMSessionMonitorRecursiveSynchronized(obj) do { } while (0) + #define GTMSessionCheckSynchronized(obj) do { } while (0) + #define GTMSessionCheckNotSynchronized(obj) do { } while (0) +#endif // !DEBUG +#endif // __OBJC__ + + +GTM_ASSUME_NONNULL_END diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcher.m b/Pods/GTMSessionFetcher/Source/GTMSessionFetcher.m @@ -0,0 +1,4579 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMSessionFetcher.h" + +#import <sys/utsname.h> + +#ifndef STRIP_GTM_FETCH_LOGGING + #error GTMSessionFetcher headers should have defaulted this if it wasn't already defined. +#endif + +GTM_ASSUME_NONNULL_BEGIN + +NSString *const kGTMSessionFetcherStartedNotification = @"kGTMSessionFetcherStartedNotification"; +NSString *const kGTMSessionFetcherStoppedNotification = @"kGTMSessionFetcherStoppedNotification"; +NSString *const kGTMSessionFetcherRetryDelayStartedNotification = @"kGTMSessionFetcherRetryDelayStartedNotification"; +NSString *const kGTMSessionFetcherRetryDelayStoppedNotification = @"kGTMSessionFetcherRetryDelayStoppedNotification"; + +NSString *const kGTMSessionFetcherCompletionInvokedNotification = @"kGTMSessionFetcherCompletionInvokedNotification"; +NSString *const kGTMSessionFetcherCompletionDataKey = @"data"; +NSString *const kGTMSessionFetcherCompletionErrorKey = @"error"; + +NSString *const kGTMSessionFetcherErrorDomain = @"com.google.GTMSessionFetcher"; +NSString *const kGTMSessionFetcherStatusDomain = @"com.google.HTTPStatus"; +NSString *const kGTMSessionFetcherStatusDataKey = @"data"; // data returned with a kGTMSessionFetcherStatusDomain error +NSString *const kGTMSessionFetcherStatusDataContentTypeKey = @"data_content_type"; + +NSString *const kGTMSessionFetcherNumberOfRetriesDoneKey = @"kGTMSessionFetcherNumberOfRetriesDoneKey"; +NSString *const kGTMSessionFetcherElapsedIntervalWithRetriesKey = @"kGTMSessionFetcherElapsedIntervalWithRetriesKey"; + +static NSString *const kGTMSessionIdentifierPrefix = @"com.google.GTMSessionFetcher"; +static NSString *const kGTMSessionIdentifierDestinationFileURLMetadataKey = @"_destURL"; +static NSString *const kGTMSessionIdentifierBodyFileURLMetadataKey = @"_bodyURL"; + +// The default max retry interview is 10 minutes for uploads (POST/PUT/PATCH), +// 1 minute for downloads. +static const NSTimeInterval kUnsetMaxRetryInterval = -1.0; +static const NSTimeInterval kDefaultMaxDownloadRetryInterval = 60.0; +static const NSTimeInterval kDefaultMaxUploadRetryInterval = 60.0 * 10.; + +// The maximum data length that can be loaded to the error userInfo +static const int64_t kMaximumDownloadErrorDataLength = 20000; + +#ifdef GTMSESSION_PERSISTED_DESTINATION_KEY +// Projects using unique class names should also define a unique persisted destination key. +static NSString * const kGTMSessionFetcherPersistedDestinationKey = + GTMSESSION_PERSISTED_DESTINATION_KEY; +#else +static NSString * const kGTMSessionFetcherPersistedDestinationKey = + @"com.google.GTMSessionFetcher.downloads"; +#endif + +GTM_ASSUME_NONNULL_END + +// +// GTMSessionFetcher +// + +#if 0 +#define GTM_LOG_BACKGROUND_SESSION(...) GTMSESSION_LOG_DEBUG(__VA_ARGS__) +#else +#define GTM_LOG_BACKGROUND_SESSION(...) +#endif + +#ifndef GTM_TARGET_SUPPORTS_APP_TRANSPORT_SECURITY + #if (TARGET_OS_TV \ + || TARGET_OS_WATCH \ + || (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0)) + #define GTM_TARGET_SUPPORTS_APP_TRANSPORT_SECURITY 1 + #endif +#endif + +@interface GTMSessionFetcher () + +@property(atomic, strong, readwrite, GTM_NULLABLE) NSData *downloadedData; +@property(atomic, strong, readwrite, GTM_NULLABLE) NSData *downloadResumeData; + +#if GTM_BACKGROUND_TASK_FETCHING +// Should always be accessed within an @synchronized(self). +@property(assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier; +#endif + +@property(atomic, readwrite, getter=isUsingBackgroundSession) BOOL usingBackgroundSession; + +@end + +#if !GTMSESSION_BUILD_COMBINED_SOURCES +@interface GTMSessionFetcher (GTMSessionFetcherLoggingInternal) +- (void)logFetchWithError:(NSError *)error; +- (void)logNowWithError:(GTM_NULLABLE NSError *)error; +- (NSInputStream *)loggedInputStreamForInputStream:(NSInputStream *)inputStream; +- (GTMSessionFetcherBodyStreamProvider)loggedStreamProviderForStreamProvider: + (GTMSessionFetcherBodyStreamProvider)streamProvider; +@end +#endif // !GTMSESSION_BUILD_COMBINED_SOURCES + +GTM_ASSUME_NONNULL_BEGIN + +static NSTimeInterval InitialMinRetryInterval(void) { + return 1.0 + ((double)(arc4random_uniform(0x0FFFF)) / (double) 0x0FFFF); +} + +static BOOL IsLocalhost(NSString * GTM_NULLABLE_TYPE host) { + // We check if there's host, and then make the comparisons. + if (host == nil) return NO; + return ([host caseInsensitiveCompare:@"localhost"] == NSOrderedSame + || [host isEqual:@"::1"] + || [host isEqual:@"127.0.0.1"]); +} + +static NSDictionary *GTM_NULLABLE_TYPE GTMErrorUserInfoForData( + NSData *GTM_NULLABLE_TYPE data, NSDictionary *GTM_NULLABLE_TYPE responseHeaders) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + + if (data.length > 0) { + userInfo[kGTMSessionFetcherStatusDataKey] = data; + + NSString *contentType = responseHeaders[@"Content-Type"]; + if (contentType) { + userInfo[kGTMSessionFetcherStatusDataContentTypeKey] = contentType; + } + } + + return userInfo.count > 0 ? userInfo : nil; +} + +static GTMSessionFetcherTestBlock GTM_NULLABLE_TYPE gGlobalTestBlock; + +@implementation GTMSessionFetcher { + NSMutableURLRequest *_request; // after beginFetch, changed only in delegate callbacks + BOOL _useUploadTask; // immutable after beginFetch + NSURL *_bodyFileURL; // immutable after beginFetch + GTMSessionFetcherBodyStreamProvider _bodyStreamProvider; // immutable after beginFetch + NSURLSession *_session; + BOOL _shouldInvalidateSession; // immutable after beginFetch + NSURLSession *_sessionNeedingInvalidation; + NSURLSessionConfiguration *_configuration; + NSURLSessionTask *_sessionTask; + NSString *_taskDescription; + float _taskPriority; + NSURLResponse *_response; + NSString *_sessionIdentifier; + BOOL _wasCreatedFromBackgroundSession; + BOOL _didCreateSessionIdentifier; + NSString *_sessionIdentifierUUID; + BOOL _userRequestedBackgroundSession; + BOOL _usingBackgroundSession; + NSMutableData * GTM_NULLABLE_TYPE _downloadedData; + NSError *_downloadFinishedError; + NSData *_downloadResumeData; // immutable after construction + NSData * GTM_NULLABLE_TYPE _downloadTaskErrorData; // Data for when download task fails + NSURL *_destinationFileURL; + int64_t _downloadedLength; + NSURLCredential *_credential; // username & password + NSURLCredential *_proxyCredential; // credential supplied to proxy servers + BOOL _isStopNotificationNeeded; // set when start notification has been sent + BOOL _isUsingTestBlock; // set when a test block was provided (remains set when the block is released) + id _userData; // retained, if set by caller + NSMutableDictionary *_properties; // more data retained for caller + dispatch_queue_t _callbackQueue; + dispatch_group_t _callbackGroup; // read-only after creation + NSOperationQueue *_delegateQueue; // immutable after beginFetch + + id<GTMFetcherAuthorizationProtocol> _authorizer; // immutable after beginFetch + + // The service object that created and monitors this fetcher, if any. + id<GTMSessionFetcherServiceProtocol> _service; // immutable; set by the fetcher service upon creation + NSString *_serviceHost; + NSInteger _servicePriority; // immutable after beginFetch + BOOL _hasStoppedFetching; // counterpart to _initialBeginFetchDate + BOOL _userStoppedFetching; + + BOOL _isRetryEnabled; // user wants auto-retry + NSTimer *_retryTimer; + NSUInteger _retryCount; + NSTimeInterval _maxRetryInterval; // default 60 (download) or 600 (upload) seconds + NSTimeInterval _minRetryInterval; // random between 1 and 2 seconds + NSTimeInterval _retryFactor; // default interval multiplier is 2 + NSTimeInterval _lastRetryInterval; + NSDate *_initialBeginFetchDate; // date that beginFetch was first invoked; immutable after initial beginFetch + NSDate *_initialRequestDate; // date of first request to the target server (ignoring auth) + BOOL _hasAttemptedAuthRefresh; // accessed only in shouldRetryNowForStatus: + + NSString *_comment; // comment for log + NSString *_log; +#if !STRIP_GTM_FETCH_LOGGING + NSMutableData *_loggedStreamData; + NSURL *_redirectedFromURL; + NSString *_logRequestBody; + NSString *_logResponseBody; + BOOL _hasLoggedError; + BOOL _deferResponseBodyLogging; +#endif +} + +#if !GTMSESSION_UNIT_TESTING ++ (void)load { + [self fetchersForBackgroundSessions]; +} +#endif + ++ (instancetype)fetcherWithRequest:(GTM_NULLABLE NSURLRequest *)request { + return [[self alloc] initWithRequest:request configuration:nil]; +} + ++ (instancetype)fetcherWithURL:(NSURL *)requestURL { + return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]]; +} + ++ (instancetype)fetcherWithURLString:(NSString *)requestURLString { + return [self fetcherWithURL:(NSURL *)[NSURL URLWithString:requestURLString]]; +} + ++ (instancetype)fetcherWithDownloadResumeData:(NSData *)resumeData { + GTMSessionFetcher *fetcher = [self fetcherWithRequest:nil]; + fetcher.comment = @"Resuming download"; + fetcher.downloadResumeData = resumeData; + return fetcher; +} + ++ (GTM_NULLABLE instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier { + GTMSESSION_ASSERT_DEBUG(sessionIdentifier != nil, @"Invalid session identifier"); + NSMapTable *sessionIdentifierToFetcherMap = [self sessionIdentifierToFetcherMap]; + GTMSessionFetcher *fetcher = [sessionIdentifierToFetcherMap objectForKey:sessionIdentifier]; + if (!fetcher && [sessionIdentifier hasPrefix:kGTMSessionIdentifierPrefix]) { + fetcher = [self fetcherWithRequest:nil]; + [fetcher setSessionIdentifier:sessionIdentifier]; + [sessionIdentifierToFetcherMap setObject:fetcher forKey:sessionIdentifier]; + fetcher->_wasCreatedFromBackgroundSession = YES; + [fetcher setCommentWithFormat:@"Resuming %@", + fetcher && fetcher->_sessionIdentifierUUID ? fetcher->_sessionIdentifierUUID : @"?"]; + } + return fetcher; +} + ++ (NSMapTable *)sessionIdentifierToFetcherMap { + // TODO: What if a service is involved in creating the fetcher? Currently, when re-creating + // fetchers, if a service was involved, it is not re-created. Should the service maintain a map? + static NSMapTable *gSessionIdentifierToFetcherMap = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSessionIdentifierToFetcherMap = [NSMapTable strongToWeakObjectsMapTable]; + }); + return gSessionIdentifierToFetcherMap; +} + +#if !GTM_ALLOW_INSECURE_REQUESTS ++ (BOOL)appAllowsInsecureRequests { + // If the main bundle Info.plist key NSAppTransportSecurity is present, and it specifies + // NSAllowsArbitraryLoads, then we need to explicitly enforce secure schemes. +#if GTM_TARGET_SUPPORTS_APP_TRANSPORT_SECURITY + static BOOL allowsInsecureRequests; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *mainBundle = [NSBundle mainBundle]; + NSDictionary *appTransportSecurity = + [mainBundle objectForInfoDictionaryKey:@"NSAppTransportSecurity"]; + allowsInsecureRequests = + [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue]; + }); + return allowsInsecureRequests; +#else + // For builds targeting iOS 8 or 10.10 and earlier, we want to require fetcher + // security checks. + return YES; +#endif // GTM_TARGET_SUPPORTS_APP_TRANSPORT_SECURITY +} +#else // GTM_ALLOW_INSECURE_REQUESTS ++ (BOOL)appAllowsInsecureRequests { + return YES; +} +#endif // !GTM_ALLOW_INSECURE_REQUESTS + + +- (instancetype)init { + return [self initWithRequest:nil configuration:nil]; +} + +- (instancetype)initWithRequest:(NSURLRequest *)request { + return [self initWithRequest:request configuration:nil]; +} + +- (instancetype)initWithRequest:(GTM_NULLABLE NSURLRequest *)request + configuration:(GTM_NULLABLE NSURLSessionConfiguration *)configuration { + self = [super init]; + if (self) { +#if GTM_BACKGROUND_TASK_FETCHING + _backgroundTaskIdentifier = UIBackgroundTaskInvalid; +#endif + _request = [request mutableCopy]; + _configuration = configuration; + + NSData *bodyData = request.HTTPBody; + if (bodyData) { + _bodyLength = (int64_t)bodyData.length; + } else { + _bodyLength = NSURLSessionTransferSizeUnknown; + } + + _callbackQueue = dispatch_get_main_queue(); + _callbackGroup = dispatch_group_create(); + _delegateQueue = [NSOperationQueue mainQueue]; + + _minRetryInterval = InitialMinRetryInterval(); + _maxRetryInterval = kUnsetMaxRetryInterval; + + _taskPriority = -1.0f; // Valid values if set are 0.0...1.0. + + _testBlockAccumulateDataChunkCount = 1; + +#if !STRIP_GTM_FETCH_LOGGING + // Encourage developers to set the comment property or use + // setCommentWithFormat: by providing a default string. + _comment = @"(No fetcher comment set)"; +#endif + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + // disallow use of fetchers in a copy property + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (NSString *)description { + NSString *requestStr = self.request.URL.description; + if (requestStr.length == 0) { + if (self.downloadResumeData.length > 0) { + requestStr = @"<download resume data>"; + } else if (_wasCreatedFromBackgroundSession) { + requestStr = @"<from bg session>"; + } else { + requestStr = @"<no request>"; + } + } + return [NSString stringWithFormat:@"%@ %p (%@)", [self class], self, requestStr]; +} + +- (void)dealloc { + GTMSESSION_ASSERT_DEBUG(!_isStopNotificationNeeded, + @"unbalanced fetcher notification for %@", _request.URL); + [self forgetSessionIdentifierForFetcherWithoutSyncCheck]; + + // Note: if a session task or a retry timer was pending, then this instance + // would be retained by those so it wouldn't be getting dealloc'd, + // hence we don't need to stopFetch here +} + +#pragma mark - + +// Begin fetching the URL (or begin a retry fetch). The delegate is retained +// for the duration of the fetch connection. + +- (void)beginFetchWithCompletionHandler:(GTM_NULLABLE GTMSessionFetcherCompletionHandler)handler { + GTMSessionCheckNotSynchronized(self); + + _completionHandler = [handler copy]; + + // The user may have called setDelegate: earlier if they want to use other + // delegate-style callbacks during the fetch; otherwise, the delegate is nil, + // which is fine. + [self beginFetchMayDelay:YES mayAuthorize:YES]; +} + +// Begin fetching the URL for a retry fetch. The delegate and completion handler +// are already provided, and do not need to be copied. +- (void)beginFetchForRetry { + GTMSessionCheckNotSynchronized(self); + + [self beginFetchMayDelay:YES mayAuthorize:YES]; +} + +- (GTMSessionFetcherCompletionHandler)completionHandlerWithTarget:(GTM_NULLABLE_TYPE id)target + didFinishSelector:(GTM_NULLABLE_TYPE SEL)finishedSelector { + GTMSessionFetcherAssertValidSelector(target, finishedSelector, @encode(GTMSessionFetcher *), + @encode(NSData *), @encode(NSError *), 0); + GTMSessionFetcherCompletionHandler completionHandler = ^(NSData *data, NSError *error) { + if (target && finishedSelector) { + id selfArg = self; // Placate ARC. + NSMethodSignature *sig = [target methodSignatureForSelector:finishedSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:(SEL)finishedSelector]; + [invocation setTarget:target]; + [invocation setArgument:&selfArg atIndex:2]; + [invocation setArgument:&data atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } + }; + return completionHandler; +} + +- (void)beginFetchWithDelegate:(GTM_NULLABLE_TYPE id)target + didFinishSelector:(GTM_NULLABLE_TYPE SEL)finishedSelector { + GTMSessionCheckNotSynchronized(self); + + GTMSessionFetcherCompletionHandler handler = [self completionHandlerWithTarget:target + didFinishSelector:finishedSelector]; + [self beginFetchWithCompletionHandler:handler]; +} + +- (void)beginFetchMayDelay:(BOOL)mayDelay + mayAuthorize:(BOOL)mayAuthorize { + // This is the internal entry point for re-starting fetches. + GTMSessionCheckNotSynchronized(self); + + NSMutableURLRequest *fetchRequest = _request; // The request property is now externally immutable. + NSURL *fetchRequestURL = fetchRequest.URL; + NSString *priorSessionIdentifier = self.sessionIdentifier; + + // A utility block for creating error objects when we fail to start the fetch. + NSError *(^beginFailureError)(NSInteger) = ^(NSInteger code){ + NSString *urlString = fetchRequestURL.absoluteString; + NSDictionary *userInfo = @{ + NSURLErrorFailingURLStringErrorKey : (urlString ? urlString : @"(missing URL)") + }; + return [NSError errorWithDomain:kGTMSessionFetcherErrorDomain + code:code + userInfo:userInfo]; + }; + + // Catch delegate queue maxConcurrentOperationCount values other than 1, particularly + // NSOperationQueueDefaultMaxConcurrentOperationCount (-1), to avoid the additional complexity + // of simultaneous or out-of-order delegate callbacks. + GTMSESSION_ASSERT_DEBUG(_delegateQueue.maxConcurrentOperationCount == 1, + @"delegate queue %@ should support one concurrent operation, not %ld", + _delegateQueue.name, + (long)_delegateQueue.maxConcurrentOperationCount); + + if (!_initialBeginFetchDate) { + // This ivar is set only here on the initial beginFetch so need not be synchronized. + _initialBeginFetchDate = [[NSDate alloc] init]; + } + + if (self.sessionTask != nil) { + // If cached fetcher returned through fetcherWithSessionIdentifier:, then it's + // already begun, but don't consider this a failure, since the user need not know this. + if (self.sessionIdentifier != nil) { + return; + } + GTMSESSION_ASSERT_DEBUG(NO, @"Fetch object %@ being reused; this should never happen", self); + [self failToBeginFetchWithError:beginFailureError(GTMSessionFetcherErrorDownloadFailed)]; + return; + } + + if (fetchRequestURL == nil && !_downloadResumeData && !priorSessionIdentifier) { + GTMSESSION_ASSERT_DEBUG(NO, @"Beginning a fetch requires a request with a URL"); + [self failToBeginFetchWithError:beginFailureError(GTMSessionFetcherErrorDownloadFailed)]; + return; + } + + // We'll respect the user's request for a background session (unless this is + // an upload fetcher, which does its initial request foreground.) + self.usingBackgroundSession = self.useBackgroundSession && [self canFetchWithBackgroundSession]; + + NSURL *bodyFileURL = self.bodyFileURL; + if (bodyFileURL) { + NSError *fileCheckError; + if (![bodyFileURL checkResourceIsReachableAndReturnError:&fileCheckError]) { + // This assert fires when the file being uploaded no longer exists once + // the fetcher is ready to start the upload. + GTMSESSION_ASSERT_DEBUG_OR_LOG(0, @"Body file is unreachable: %@\n %@", + bodyFileURL.path, fileCheckError); + [self failToBeginFetchWithError:fileCheckError]; + return; + } + } + + NSString *requestScheme = fetchRequestURL.scheme; + BOOL isDataRequest = [requestScheme isEqual:@"data"]; + if (isDataRequest) { + // NSURLSession does not support data URLs in background sessions. +#if DEBUG + if (priorSessionIdentifier || self.sessionIdentifier) { + GTMSESSION_LOG_DEBUG(@"Converting background to foreground session for %@", + fetchRequest); + } +#endif + [self setSessionIdentifierInternal:nil]; + self.useBackgroundSession = NO; + } + +#if GTM_ALLOW_INSECURE_REQUESTS + BOOL shouldCheckSecurity = NO; +#else + BOOL shouldCheckSecurity = (fetchRequestURL != nil + && !isDataRequest + && [[self class] appAllowsInsecureRequests]); +#endif + + if (shouldCheckSecurity) { + // Allow https only for requests, unless overridden by the client. + // + // Non-https requests may too easily be snooped, so we disallow them by default. + // + // file: and data: schemes are usually safe if they are hardcoded in the client or provided + // by a trusted source, but since it's fairly rare to need them, it's safest to make clients + // explicitly whitelist them. + BOOL isSecure = + requestScheme != nil && [requestScheme caseInsensitiveCompare:@"https"] == NSOrderedSame; + if (!isSecure) { + BOOL allowRequest = NO; + NSString *host = fetchRequestURL.host; + + // Check schemes first. A file scheme request may be allowed here, or as a localhost request. + for (NSString *allowedScheme in _allowedInsecureSchemes) { + if (requestScheme != nil && + [requestScheme caseInsensitiveCompare:allowedScheme] == NSOrderedSame) { + allowRequest = YES; + break; + } + } + if (!allowRequest) { + // Check for localhost requests. Security checks only occur for non-https requests, so + // this check won't happen for an https request to localhost. + BOOL isLocalhostRequest = (host.length == 0 && [fetchRequestURL isFileURL]) || IsLocalhost(host); + if (isLocalhostRequest) { + if (self.allowLocalhostRequest) { + allowRequest = YES; + } else { + GTMSESSION_ASSERT_DEBUG(NO, @"Fetch request for localhost but fetcher" + @" allowLocalhostRequest is not set: %@", fetchRequestURL); + } + } else { + GTMSESSION_ASSERT_DEBUG(NO, @"Insecure fetch request has a scheme (%@)" + @" not found in fetcher allowedInsecureSchemes (%@): %@", + requestScheme, _allowedInsecureSchemes ?: @" @[] ", fetchRequestURL); + } + } + + if (!allowRequest) { +#if !DEBUG + NSLog(@"Insecure fetch disallowed for %@", fetchRequestURL.description ?: @"nil request URL"); +#endif + [self failToBeginFetchWithError:beginFailureError(GTMSessionFetcherErrorInsecureRequest)]; + return; + } + } // !isSecure + } // (requestURL != nil) && !isDataRequest + + if (self.cookieStorage == nil) { + self.cookieStorage = [[self class] staticCookieStorage]; + } + + BOOL isRecreatingSession = (self.sessionIdentifier != nil) && (fetchRequest == nil); + + self.canShareSession = !isRecreatingSession && !self.usingBackgroundSession; + + if (!self.session && self.canShareSession) { + self.session = [_service sessionForFetcherCreation]; + // If _session is nil, then the service's session creation semaphore will block + // until this fetcher invokes fetcherDidCreateSession: below, so this *must* invoke + // that method, even if the session fails to be created. + } + + if (!self.session) { + // Create a session. + if (!_configuration) { + if (priorSessionIdentifier || self.usingBackgroundSession) { + NSString *sessionIdentifier = priorSessionIdentifier; + if (!sessionIdentifier) { + sessionIdentifier = [self createSessionIdentifierWithMetadata:nil]; + } + NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIdentifierToFetcherMap]; + [sessionIdentifierToFetcherMap setObject:self forKey:self.sessionIdentifier]; + +#if (TARGET_OS_TV \ + || TARGET_OS_WATCH \ + || (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0)) + // iOS 8/10.10 builds require the new backgroundSessionConfiguration method name. + _configuration = + [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionIdentifier]; +#elif (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0) + // Do a runtime check to avoid a deprecation warning about using + // +backgroundSessionConfiguration: on iOS 8. + if ([NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) { + // Running on iOS 8+/OS X 10.10+. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +// Disable unguarded availability warning as we can't use the @availability macro until we require +// all clients to build with Xcode 9 or above. + _configuration = + [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionIdentifier]; +#pragma clang diagnostic pop + } else { + // Running on iOS 7/OS X 10.9. + _configuration = + [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier]; + } +#else + // Building with an SDK earlier than iOS 8/OS X 10.10. + _configuration = + [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier]; +#endif + self.usingBackgroundSession = YES; + self.canShareSession = NO; + } else { + _configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + } +#if !GTM_ALLOW_INSECURE_REQUESTS + _configuration.TLSMinimumSupportedProtocol = kTLSProtocol12; +#endif + } // !_configuration + _configuration.HTTPCookieStorage = self.cookieStorage; + + if (_configurationBlock) { + _configurationBlock(self, _configuration); + } + + id<NSURLSessionDelegate> delegate = [_service sessionDelegate]; + if (!delegate || !self.canShareSession) { + delegate = self; + } + self.session = [NSURLSession sessionWithConfiguration:_configuration + delegate:delegate + delegateQueue:self.sessionDelegateQueue]; + GTMSESSION_ASSERT_DEBUG(self.session, @"Couldn't create session"); + + // Tell the service about the session created by this fetcher. This also signals the + // service's semaphore to allow other fetchers to request this session. + [_service fetcherDidCreateSession:self]; + + // If this assertion fires, the client probably tried to use a session identifier that was + // already used. The solution is to make the client use a unique identifier (or better yet let + // the session fetcher assign the identifier). + GTMSESSION_ASSERT_DEBUG(self.session.delegate == delegate, @"Couldn't assign delegate."); + + if (self.session) { + BOOL isUsingSharedDelegate = (delegate != self); + if (!isUsingSharedDelegate) { + _shouldInvalidateSession = YES; + } + } + } + + if (isRecreatingSession) { + _shouldInvalidateSession = YES; + + // Let's make sure there are tasks still running or if not that we get a callback from a + // completed one; otherwise, we assume the tasks failed. + // This is the observed behavior perhaps 25% of the time within the Simulator running 7.0.3 on + // exiting the app after starting an upload and relaunching the app if we manage to relaunch + // after the task has completed, but before the system relaunches us in the background. + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, + NSArray *downloadTasks) { + if (dataTasks.count == 0 && uploadTasks.count == 0 && downloadTasks.count == 0) { + double const kDelayInSeconds = 1.0; // We should get progress indication or completion soon + dispatch_time_t checkForFeedbackDelay = + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelayInSeconds * NSEC_PER_SEC)); + dispatch_after(checkForFeedbackDelay, dispatch_get_main_queue(), ^{ + if (!self.sessionTask && !fetchRequest) { + // If our task and/or request haven't been restored, then we assume task feedback lost. + [self removePersistedBackgroundSessionFromDefaults]; + NSError *sessionError = + [NSError errorWithDomain:kGTMSessionFetcherErrorDomain + code:GTMSessionFetcherErrorBackgroundFetchFailed + userInfo:nil]; + [self failToBeginFetchWithError:sessionError]; + } + }); + } + }]; + return; + } + + self.downloadedData = nil; + self.downloadedLength = 0; + + if (_servicePriority == NSIntegerMin) { + mayDelay = NO; + } + if (mayDelay && _service) { + BOOL shouldFetchNow = [_service fetcherShouldBeginFetching:self]; + if (!shouldFetchNow) { + // The fetch is deferred, but will happen later. + // + // If this session is held by the fetcher service, clear the session now so that we don't + // assume it's still valid after the fetcher is restarted. + if (self.canShareSession) { + self.session = nil; + } + return; + } + } + + NSString *effectiveHTTPMethod = [fetchRequest valueForHTTPHeaderField:@"X-HTTP-Method-Override"]; + if (effectiveHTTPMethod == nil) { + effectiveHTTPMethod = fetchRequest.HTTPMethod; + } + BOOL isEffectiveHTTPGet = (effectiveHTTPMethod == nil + || [effectiveHTTPMethod isEqual:@"GET"]); + + BOOL needsUploadTask = (self.useUploadTask || self.bodyFileURL || self.bodyStreamProvider); + if (_bodyData || self.bodyStreamProvider || fetchRequest.HTTPBodyStream) { + if (isEffectiveHTTPGet) { + fetchRequest.HTTPMethod = @"POST"; + isEffectiveHTTPGet = NO; + } + + if (_bodyData) { + if (!needsUploadTask) { + fetchRequest.HTTPBody = _bodyData; + } +#if !STRIP_GTM_FETCH_LOGGING + } else if (fetchRequest.HTTPBodyStream) { + if ([self respondsToSelector:@selector(loggedInputStreamForInputStream:)]) { + fetchRequest.HTTPBodyStream = + [self performSelector:@selector(loggedInputStreamForInputStream:) + withObject:fetchRequest.HTTPBodyStream]; + } +#endif + } + } + + // We authorize after setting up the http method and body in the request + // because OAuth 1 may need to sign the request body + if (mayAuthorize && _authorizer && !isDataRequest) { + BOOL isAuthorized = [_authorizer isAuthorizedRequest:fetchRequest]; + if (!isAuthorized) { + // Authorization needed. + // + // If this session is held by the fetcher service, clear the session now so that we don't + // assume it's still valid after authorization completes. + if (self.canShareSession) { + self.session = nil; + } + + // Authorizing the request will recursively call this beginFetch:mayDelay: + // or failToBeginFetchWithError:. + [self authorizeRequest]; + return; + } + } + + // set the default upload or download retry interval, if necessary + if ([self isRetryEnabled] && self.maxRetryInterval <= 0) { + if (isEffectiveHTTPGet || [effectiveHTTPMethod isEqual:@"HEAD"]) { + [self setMaxRetryInterval:kDefaultMaxDownloadRetryInterval]; + } else { + [self setMaxRetryInterval:kDefaultMaxUploadRetryInterval]; + } + } + + // finally, start the connection + NSURLSessionTask *newSessionTask; + BOOL needsDataAccumulator = NO; + if (_downloadResumeData) { + newSessionTask = [_session downloadTaskWithResumeData:_downloadResumeData]; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, + @"Failed downloadTaskWithResumeData for %@, resume data %lu bytes", + _session, (unsigned long)_downloadResumeData.length); + } else if (_destinationFileURL && !isDataRequest) { + newSessionTask = [_session downloadTaskWithRequest:fetchRequest]; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, @"Failed downloadTaskWithRequest for %@, %@", + _session, fetchRequest); + } else if (needsUploadTask) { + if (bodyFileURL) { + newSessionTask = [_session uploadTaskWithRequest:fetchRequest + fromFile:bodyFileURL]; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, + @"Failed uploadTaskWithRequest for %@, %@, file %@", + _session, fetchRequest, bodyFileURL.path); + } else if (self.bodyStreamProvider) { + newSessionTask = [_session uploadTaskWithStreamedRequest:fetchRequest]; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, + @"Failed uploadTaskWithStreamedRequest for %@, %@", + _session, fetchRequest); + } else { + GTMSESSION_ASSERT_DEBUG_OR_LOG(_bodyData != nil, + @"Upload task needs body data, %@", fetchRequest); + newSessionTask = [_session uploadTaskWithRequest:fetchRequest + fromData:(NSData * GTM_NONNULL_TYPE)_bodyData]; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, + @"Failed uploadTaskWithRequest for %@, %@, body data %lu bytes", + _session, fetchRequest, (unsigned long)_bodyData.length); + } + needsDataAccumulator = YES; + } else { + newSessionTask = [_session dataTaskWithRequest:fetchRequest]; + needsDataAccumulator = YES; + GTMSESSION_ASSERT_DEBUG_OR_LOG(newSessionTask, @"Failed dataTaskWithRequest for %@, %@", + _session, fetchRequest); + } + self.sessionTask = newSessionTask; + + if (!newSessionTask) { + // We shouldn't get here; if we're here, an earlier assertion should have fired to explain + // which session task creation failed. + [self failToBeginFetchWithError:beginFailureError(GTMSessionFetcherErrorTaskCreationFailed)]; + return; + } + + if (needsDataAccumulator && _accumulateDataBlock == nil) { + self.downloadedData = [NSMutableData data]; + } + if (_taskDescription) { + newSessionTask.taskDescription = _taskDescription; + } + if (_taskPriority >= 0) { +#if TARGET_OS_TV || TARGET_OS_WATCH + BOOL hasTaskPriority = YES; +#elif (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0) + BOOL hasTaskPriority = YES; +#else + BOOL hasTaskPriority = [newSessionTask respondsToSelector:@selector(setPriority:)]; +#endif + if (hasTaskPriority) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +// Disable unguarded availability warning as we can't use the @availability macro until we require +// all clients to build with Xcode 9 or above. + newSessionTask.priority = _taskPriority; +#pragma clang diagnostic pop + } + } + +#if GTM_DISABLE_FETCHER_TEST_BLOCK + GTMSESSION_ASSERT_DEBUG(_testBlock == nil && gGlobalTestBlock == nil, @"test blocks disabled"); + _testBlock = nil; +#else + if (!_testBlock) { + if (gGlobalTestBlock) { + // Note that the test block may pass nil for all of its response parameters, + // indicating that the fetch should actually proceed. This is useful when the + // global test block has been set, and the app is only testing a specific + // fetcher. The block simulation code will then resume the task. + _testBlock = gGlobalTestBlock; + } + } + _isUsingTestBlock = (_testBlock != nil); +#endif // GTM_DISABLE_FETCHER_TEST_BLOCK + +#if GTM_BACKGROUND_TASK_FETCHING + id<GTMUIApplicationProtocol> app = [[self class] fetcherUIApplication]; + // Background tasks seem to interfere with out-of-process uploads and downloads. + if (app && !self.skipBackgroundTask && !self.useBackgroundSession) { + // Tell UIApplication that we want to continue even when the app is in the + // background. +#if DEBUG + NSString *bgTaskName = [NSString stringWithFormat:@"%@-%@", + [self class], fetchRequest.URL.host]; +#else + NSString *bgTaskName = @"GTMSessionFetcher"; +#endif + __block UIBackgroundTaskIdentifier bgTaskID = [app beginBackgroundTaskWithName:bgTaskName + expirationHandler:^{ + // Background task expiration callback - this block is always invoked by + // UIApplication on the main thread. + if (bgTaskID != UIBackgroundTaskInvalid) { + @synchronized(self) { + if (bgTaskID == self.backgroundTaskIdentifier) { + self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + } + [app endBackgroundTask:bgTaskID]; + } + }]; + @synchronized(self) { + self.backgroundTaskIdentifier = bgTaskID; + } + } +#endif + + if (!_initialRequestDate) { + _initialRequestDate = [[NSDate alloc] init]; + } + + // We don't expect to reach here even on retry or auth until a stop notification has been sent + // for the previous task, but we should ensure that we don't unbalance that. + GTMSESSION_ASSERT_DEBUG(!_isStopNotificationNeeded, @"Start notification without a prior stop"); + [self sendStopNotificationIfNeeded]; + + [self addPersistedBackgroundSessionToDefaults]; + + [self setStopNotificationNeeded:YES]; + + [self postNotificationOnMainThreadWithName:kGTMSessionFetcherStartedNotification + userInfo:nil + requireAsync:NO]; + + // The service needs to know our task if it is serving as NSURLSession delegate. + [_service fetcherDidBeginFetching:self]; + + if (_testBlock) { +#if !GTM_DISABLE_FETCHER_TEST_BLOCK + [self simulateFetchForTestBlock]; +#endif + } else { + // We resume the session task after posting the notification since the + // delegate callbacks may happen immediately if the fetch is started off + // the main thread or the session delegate queue is on a background thread, + // and we don't want to post a start notification after a premature finish + // of the session task. + [newSessionTask resume]; + } +} + +NSData * GTM_NULLABLE_TYPE GTMDataFromInputStream(NSInputStream *inputStream, NSError **outError) { + NSMutableData *data = [NSMutableData data]; + + [inputStream open]; + NSInteger numberOfBytesRead = 0; + while ([inputStream hasBytesAvailable]) { + uint8_t buffer[512]; + numberOfBytesRead = [inputStream read:buffer maxLength:sizeof(buffer)]; + if (numberOfBytesRead > 0) { + [data appendBytes:buffer length:(NSUInteger)numberOfBytesRead]; + } else { + break; + } + } + [inputStream close]; + NSError *streamError = inputStream.streamError; + + if (streamError) { + data = nil; + } + if (outError) { + *outError = streamError; + } + return data; +} + +#if !GTM_DISABLE_FETCHER_TEST_BLOCK + +- (void)simulateFetchForTestBlock { + // This is invoked on the same thread as the beginFetch method was. + // + // Callbacks will all occur on the callback queue. + _testBlock(self, ^(NSURLResponse *response, NSData *responseData, NSError *error) { + // Callback from test block. + if (response == nil && responseData == nil && error == nil) { + // Assume the fetcher should execute rather than be tested. + self->_testBlock = nil; + self->_isUsingTestBlock = NO; + [self->_sessionTask resume]; + return; + } + + GTMSessionFetcherBodyStreamProvider bodyStreamProvider = self.bodyStreamProvider; + if (bodyStreamProvider) { + bodyStreamProvider(^(NSInputStream *bodyStream){ + // Read from the input stream into an NSData buffer. We'll drain the stream + // explicitly on a background queue. + [self invokeOnCallbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) + afterUserStopped:NO + block:^{ + NSError *streamError; + NSData *streamedData = GTMDataFromInputStream(bodyStream, &streamError); + + dispatch_async(dispatch_get_main_queue(), ^{ + // Continue callbacks on the main thread, since serial behavior + // is more reliable for tests. + [self simulateDataCallbacksForTestBlockWithBodyData:streamedData + response:response + responseData:responseData + error:(error ?: streamError)]; + }); + }]; + }); + } else { + // No input stream; use the supplied data or file URL. + NSURL *bodyFileURL = self.bodyFileURL; + if (bodyFileURL) { + NSError *readError; + self->_bodyData = [NSData dataWithContentsOfURL:bodyFileURL + options:NSDataReadingMappedIfSafe + error:&readError]; + error = readError; + } + + // No stream provider. + + // In real fetches, nothing happens until the run loop spins, so apps have leeway to + // set callbacks after they call beginFetch. We'll mirror that fetcher behavior by + // delaying callbacks here at least to the next spin of the run loop. That keeps + // immediate, synchronous setting of callback blocks after beginFetch working in tests. + dispatch_async(dispatch_get_main_queue(), ^{ + [self simulateDataCallbacksForTestBlockWithBodyData:self->_bodyData + response:response + responseData:responseData + error:error]; + }); + } + }); +} + +- (void)simulateByteTransferReportWithDataLength:(int64_t)totalDataLength + block:(GTMSessionFetcherSendProgressBlock)block { + // This utility method simulates transfer progress with up to three callbacks. + // It is used to call back to any of the progress blocks. + int64_t sendReportSize = totalDataLength / 3 + 1; + int64_t totalSent = 0; + while (totalSent < totalDataLength) { + int64_t bytesRemaining = totalDataLength - totalSent; + sendReportSize = MIN(sendReportSize, bytesRemaining); + totalSent += sendReportSize; + [self invokeOnCallbackQueueUnlessStopped:^{ + block(sendReportSize, totalSent, totalDataLength); + }]; + } +} + +- (void)simulateDataCallbacksForTestBlockWithBodyData:(NSData * GTM_NULLABLE_TYPE)bodyData + response:(NSURLResponse *)response + responseData:(NSData *)suppliedData + error:(NSError *)suppliedError { + __block NSData *responseData = suppliedData; + __block NSError *responseError = suppliedError; + + // This method does the test simulation of callbacks once the upload + // and download data are known. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Get copies of ivars we'll access in async invocations. This simulation assumes + // they won't change during fetcher execution. + NSURL *destinationFileURL = _destinationFileURL; + GTMSessionFetcherWillRedirectBlock willRedirectBlock = _willRedirectBlock; + GTMSessionFetcherDidReceiveResponseBlock didReceiveResponseBlock = _didReceiveResponseBlock; + GTMSessionFetcherSendProgressBlock sendProgressBlock = _sendProgressBlock; + GTMSessionFetcherDownloadProgressBlock downloadProgressBlock = _downloadProgressBlock; + GTMSessionFetcherAccumulateDataBlock accumulateDataBlock = _accumulateDataBlock; + GTMSessionFetcherReceivedProgressBlock receivedProgressBlock = _receivedProgressBlock; + GTMSessionFetcherWillCacheURLResponseBlock willCacheURLResponseBlock = + _willCacheURLResponseBlock; + + // Simulate receipt of redirection. + if (willRedirectBlock) { + [self invokeOnCallbackUnsynchronizedQueueAfterUserStopped:YES + block:^{ + willRedirectBlock((NSHTTPURLResponse *)response, self->_request, + ^(NSURLRequest *redirectRequest) { + // For simulation, we'll assume the app will just continue. + }); + }]; + } + + // If the fetcher has a challenge block, simulate a challenge. + // + // It might be nice to eventually let the user determine which testBlock + // fetches get challenged rather than always executing the supplied + // challenge block. + if (_challengeBlock) { + [self invokeOnCallbackUnsynchronizedQueueAfterUserStopped:YES + block:^{ + if (self->_challengeBlock) { + NSURL *requestURL = self->_request.URL; + NSString *host = requestURL.host; + NSURLProtectionSpace *pspace = + [[NSURLProtectionSpace alloc] initWithHost:host + port:requestURL.port.integerValue + protocol:requestURL.scheme + realm:nil + authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; + id<NSURLAuthenticationChallengeSender> unusedSender = + (id<NSURLAuthenticationChallengeSender>)[NSNull null]; + NSURLAuthenticationChallenge *challenge = + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:pspace + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:unusedSender]; + self->_challengeBlock(self, challenge, ^(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * GTM_NULLABLE_TYPE credential){ + // We could change the responseData and responseError based on the disposition, + // but it's easier for apps to just supply the expected data and error + // directly to the test block. So this simulation ignores the disposition. + }); + } + }]; + } + + // Simulate receipt of an initial response. + if (response && didReceiveResponseBlock) { + [self invokeOnCallbackUnsynchronizedQueueAfterUserStopped:YES + block:^{ + didReceiveResponseBlock(response, ^(NSURLSessionResponseDisposition desiredDisposition) { + // For simulation, we'll assume the disposition is to continue. + }); + }]; + } + + // Simulate reporting send progress. + if (sendProgressBlock) { + [self simulateByteTransferReportWithDataLength:(int64_t)bodyData.length + block:^(int64_t bytesSent, + int64_t totalBytesSent, + int64_t totalBytesExpectedToSend) { + // This is invoked on the callback queue unless stopped. + sendProgressBlock(bytesSent, totalBytesSent, totalBytesExpectedToSend); + }]; + } + + if (destinationFileURL) { + // Simulate download to file progress. + if (downloadProgressBlock) { + [self simulateByteTransferReportWithDataLength:(int64_t)responseData.length + block:^(int64_t bytesDownloaded, + int64_t totalBytesDownloaded, + int64_t totalBytesExpectedToDownload) { + // This is invoked on the callback queue unless stopped. + downloadProgressBlock(bytesDownloaded, totalBytesDownloaded, + totalBytesExpectedToDownload); + }]; + } + + NSError *writeError; + [responseData writeToURL:destinationFileURL + options:NSDataWritingAtomic + error:&writeError]; + if (writeError) { + // Tell the test code that writing failed. + responseError = writeError; + } + } else { + // Simulate download to NSData progress. + if ((accumulateDataBlock || receivedProgressBlock) && responseData) { + [self simulateByteTransferWithData:responseData + block:^(NSData *data, + int64_t bytesReceived, + int64_t totalBytesReceived, + int64_t totalBytesExpectedToReceive) { + // This is invoked on the callback queue unless stopped. + if (accumulateDataBlock) { + accumulateDataBlock(data); + } + + if (receivedProgressBlock) { + receivedProgressBlock(bytesReceived, totalBytesReceived); + } + }]; + } + + if (!accumulateDataBlock) { + _downloadedData = [responseData mutableCopy]; + } + + if (willCacheURLResponseBlock) { + // Simulate letting the client inspect and alter the cached response. + NSData *cachedData = responseData ?: [[NSData alloc] init]; // Always have non-nil data. + NSCachedURLResponse *cachedResponse = + [[NSCachedURLResponse alloc] initWithResponse:response + data:cachedData]; + [self invokeOnCallbackUnsynchronizedQueueAfterUserStopped:YES + block:^{ + willCacheURLResponseBlock(cachedResponse, ^(NSCachedURLResponse *responseToCache){ + // The app may provide an alternative response, or nil to defeat caching. + }); + }]; + } + } + _response = response; + } // @synchronized(self) + + NSOperationQueue *queue = self.sessionDelegateQueue; + [queue addOperationWithBlock:^{ + // Rather than invoke failToBeginFetchWithError: we want to simulate completion of + // a connection that started and ended, so we'll call down to finishWithError: + NSInteger status = responseError ? responseError.code : 200; + if (status >= 200 && status <= 399) { + [self finishWithError:nil shouldRetry:NO]; + } else { + [self shouldRetryNowForStatus:status + error:responseError + forceAssumeRetry:NO + response:^(BOOL shouldRetry) { + [self finishWithError:responseError shouldRetry:shouldRetry]; + }]; + } + }]; +} + +- (void)simulateByteTransferWithData:(NSData *)responseData + block:(GTMSessionFetcherSimulateByteTransferBlock)transferBlock { + // This utility method simulates transfering data to the client. It divides the data into at most + // "chunkCount" chunks and then passes each chunk along with a progress update to transferBlock. + // This function can be used with accumulateDataBlock or receivedProgressBlock. + + NSUInteger chunkCount = MAX(self.testBlockAccumulateDataChunkCount, (NSUInteger) 1); + NSUInteger totalDataLength = responseData.length; + NSUInteger sendDataSize = totalDataLength / chunkCount + 1; + NSUInteger totalSent = 0; + while (totalSent < totalDataLength) { + NSUInteger bytesRemaining = totalDataLength - totalSent; + sendDataSize = MIN(sendDataSize, bytesRemaining); + NSData *chunkData = [responseData subdataWithRange:NSMakeRange(totalSent, sendDataSize)]; + totalSent += sendDataSize; + [self invokeOnCallbackQueueUnlessStopped:^{ + transferBlock(chunkData, + (int64_t)sendDataSize, + (int64_t)totalSent, + (int64_t)totalDataLength); + }]; + } +} + +#endif // !GTM_DISABLE_FETCHER_TEST_BLOCK + +- (void)setSessionTask:(NSURLSessionTask *)sessionTask { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_sessionTask != sessionTask) { + _sessionTask = sessionTask; + if (_sessionTask) { + // Request could be nil on restoring this fetcher from a background session. + if (!_request) { + _request = [_sessionTask.originalRequest mutableCopy]; + } + } + } + } // @synchronized(self) +} + +- (NSURLSessionTask * GTM_NULLABLE_TYPE)sessionTask { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _sessionTask; + } // @synchronized(self) +} + ++ (NSUserDefaults *)fetcherUserDefaults { + static NSUserDefaults *gFetcherUserDefaults = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class fetcherUserDefaultsClass = NSClassFromString(@"GTMSessionFetcherUserDefaultsFactory"); + if (fetcherUserDefaultsClass) { + gFetcherUserDefaults = [fetcherUserDefaultsClass fetcherUserDefaults]; + } else { + gFetcherUserDefaults = [NSUserDefaults standardUserDefaults]; + } + }); + return gFetcherUserDefaults; +} + +- (void)addPersistedBackgroundSessionToDefaults { + NSString *sessionIdentifier = self.sessionIdentifier; + if (!sessionIdentifier) { + return; + } + NSArray *oldBackgroundSessions = [[self class] activePersistedBackgroundSessions]; + if ([oldBackgroundSessions containsObject:_sessionIdentifier]) { + return; + } + NSMutableArray *newBackgroundSessions = + [NSMutableArray arrayWithArray:oldBackgroundSessions]; + [newBackgroundSessions addObject:sessionIdentifier]; + GTM_LOG_BACKGROUND_SESSION(@"Add to background sessions: %@", newBackgroundSessions); + + NSUserDefaults *userDefaults = [[self class] fetcherUserDefaults]; + [userDefaults setObject:newBackgroundSessions + forKey:kGTMSessionFetcherPersistedDestinationKey]; + [userDefaults synchronize]; +} + +- (void)removePersistedBackgroundSessionFromDefaults { + NSString *sessionIdentifier = self.sessionIdentifier; + if (!sessionIdentifier) return; + + NSArray *oldBackgroundSessions = [[self class] activePersistedBackgroundSessions]; + if (!oldBackgroundSessions) { + return; + } + NSMutableArray *newBackgroundSessions = + [NSMutableArray arrayWithArray:oldBackgroundSessions]; + NSUInteger sessionIndex = [newBackgroundSessions indexOfObject:sessionIdentifier]; + if (sessionIndex == NSNotFound) { + return; + } + [newBackgroundSessions removeObjectAtIndex:sessionIndex]; + GTM_LOG_BACKGROUND_SESSION(@"Remove from background sessions: %@", newBackgroundSessions); + + NSUserDefaults *userDefaults = [[self class] fetcherUserDefaults]; + if (newBackgroundSessions.count == 0) { + [userDefaults removeObjectForKey:kGTMSessionFetcherPersistedDestinationKey]; + } else { + [userDefaults setObject:newBackgroundSessions + forKey:kGTMSessionFetcherPersistedDestinationKey]; + } + [userDefaults synchronize]; +} + ++ (GTM_NULLABLE NSArray *)activePersistedBackgroundSessions { + NSUserDefaults *userDefaults = [[self class] fetcherUserDefaults]; + NSArray *oldBackgroundSessions = + [userDefaults arrayForKey:kGTMSessionFetcherPersistedDestinationKey]; + if (oldBackgroundSessions.count == 0) { + return nil; + } + NSMutableArray *activeBackgroundSessions = nil; + NSMapTable *sessionIdentifierToFetcherMap = [self sessionIdentifierToFetcherMap]; + for (NSString *sessionIdentifier in oldBackgroundSessions) { + GTMSessionFetcher *fetcher = [sessionIdentifierToFetcherMap objectForKey:sessionIdentifier]; + if (fetcher) { + if (!activeBackgroundSessions) { + activeBackgroundSessions = [[NSMutableArray alloc] init]; + } + [activeBackgroundSessions addObject:sessionIdentifier]; + } + } + return activeBackgroundSessions; +} + ++ (NSArray *)fetchersForBackgroundSessions { + NSUserDefaults *userDefaults = [[self class] fetcherUserDefaults]; + NSArray *backgroundSessions = + [userDefaults arrayForKey:kGTMSessionFetcherPersistedDestinationKey]; + NSMapTable *sessionIdentifierToFetcherMap = [self sessionIdentifierToFetcherMap]; + NSMutableArray *fetchers = [NSMutableArray array]; + for (NSString *sessionIdentifier in backgroundSessions) { + GTMSessionFetcher *fetcher = [sessionIdentifierToFetcherMap objectForKey:sessionIdentifier]; + if (!fetcher) { + fetcher = [self fetcherWithSessionIdentifier:sessionIdentifier]; + GTMSESSION_ASSERT_DEBUG(fetcher != nil, + @"Unexpected invalid session identifier: %@", sessionIdentifier); + [fetcher beginFetchWithCompletionHandler:nil]; + } + GTM_LOG_BACKGROUND_SESSION(@"%@ restoring session %@ by creating fetcher %@ %p", + [self class], sessionIdentifier, fetcher, fetcher); + if (fetcher != nil) { + [fetchers addObject:fetcher]; + } + } + return fetchers; +} + +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH ++ (void)application:(UIApplication *)application + handleEventsForBackgroundURLSession:(NSString *)identifier + completionHandler:(GTMSessionFetcherSystemCompletionHandler)completionHandler { + GTMSessionFetcher *fetcher = [self fetcherWithSessionIdentifier:identifier]; + if (fetcher != nil) { + fetcher.systemCompletionHandler = completionHandler; + } else { + GTM_LOG_BACKGROUND_SESSION(@"%@ did not create background session identifier: %@", + [self class], identifier); + } +} +#endif + +- (NSString * GTM_NULLABLE_TYPE)sessionIdentifier { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _sessionIdentifier; + } // @synchronized(self) +} + +- (void)setSessionIdentifier:(NSString *)sessionIdentifier { + GTMSESSION_ASSERT_DEBUG(sessionIdentifier != nil, @"Invalid session identifier"); + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSESSION_ASSERT_DEBUG(!_session, @"Unable to set session identifier after session created"); + _sessionIdentifier = [sessionIdentifier copy]; + _usingBackgroundSession = YES; + _canShareSession = NO; + [self restoreDefaultStateForSessionIdentifierMetadata]; + } // @synchronized(self) +} + +- (void)setSessionIdentifierInternal:(GTM_NULLABLE NSString *)sessionIdentifier { + // This internal method only does a synchronized set of the session identifier. + // It does not have side effects on the background session, shared session, or + // session identifier metadata. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _sessionIdentifier = [sessionIdentifier copy]; + } // @synchronized(self) +} + +- (NSDictionary * GTM_NULLABLE_TYPE)sessionUserInfo { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_sessionUserInfo == nil) { + // We'll return the metadata dictionary with internal keys removed. This avoids the user + // re-using the userInfo dictionary later and accidentally including the internal keys. + NSMutableDictionary *metadata = [[self sessionIdentifierMetadataUnsynchronized] mutableCopy]; + NSSet *keysToRemove = [metadata keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) { + return [key hasPrefix:@"_"]; + }]; + [metadata removeObjectsForKeys:[keysToRemove allObjects]]; + if (metadata.count > 0) { + _sessionUserInfo = metadata; + } + } + return _sessionUserInfo; + } // @synchronized(self) +} + +- (void)setSessionUserInfo:(NSDictionary * GTM_NULLABLE_TYPE)dictionary { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSESSION_ASSERT_DEBUG(_sessionIdentifier == nil, @"Too late to assign userInfo"); + _sessionUserInfo = dictionary; + } // @synchronized(self) +} + +- (GTM_NULLABLE NSDictionary *)sessionIdentifierDefaultMetadata { + GTMSessionCheckSynchronized(self); + + NSMutableDictionary *defaultUserInfo = [[NSMutableDictionary alloc] init]; + if (_destinationFileURL) { + defaultUserInfo[kGTMSessionIdentifierDestinationFileURLMetadataKey] = + [_destinationFileURL absoluteString]; + } + if (_bodyFileURL) { + defaultUserInfo[kGTMSessionIdentifierBodyFileURLMetadataKey] = [_bodyFileURL absoluteString]; + } + return (defaultUserInfo.count > 0) ? defaultUserInfo : nil; +} + +- (void)restoreDefaultStateForSessionIdentifierMetadata { + GTMSessionCheckSynchronized(self); + + NSDictionary *metadata = [self sessionIdentifierMetadataUnsynchronized]; + NSString *destinationFileURLString = metadata[kGTMSessionIdentifierDestinationFileURLMetadataKey]; + if (destinationFileURLString) { + _destinationFileURL = [NSURL URLWithString:destinationFileURLString]; + GTM_LOG_BACKGROUND_SESSION(@"Restoring destination file URL: %@", _destinationFileURL); + } + NSString *bodyFileURLString = metadata[kGTMSessionIdentifierBodyFileURLMetadataKey]; + if (bodyFileURLString) { + _bodyFileURL = [NSURL URLWithString:bodyFileURLString]; + GTM_LOG_BACKGROUND_SESSION(@"Restoring body file URL: %@", _bodyFileURL); + } +} + +- (NSDictionary * GTM_NULLABLE_TYPE)sessionIdentifierMetadata { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [self sessionIdentifierMetadataUnsynchronized]; + } +} + +- (NSDictionary * GTM_NULLABLE_TYPE)sessionIdentifierMetadataUnsynchronized { + GTMSessionCheckSynchronized(self); + + // Session Identifier format: "com.google.<ClassName>_<UUID>_<Metadata in JSON format> + if (!_sessionIdentifier) { + return nil; + } + NSScanner *metadataScanner = [NSScanner scannerWithString:_sessionIdentifier]; + [metadataScanner setCharactersToBeSkipped:nil]; + NSString *metadataString; + NSString *uuid; + if ([metadataScanner scanUpToString:@"_" intoString:NULL] && + [metadataScanner scanString:@"_" intoString:NULL] && + [metadataScanner scanUpToString:@"_" intoString:&uuid] && + [metadataScanner scanString:@"_" intoString:NULL] && + [metadataScanner scanUpToString:@"\n" intoString:&metadataString]) { + _sessionIdentifierUUID = uuid; + NSData *metadataData = [metadataString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error; + NSDictionary *metadataDict = + [NSJSONSerialization JSONObjectWithData:metadataData + options:0 + error:&error]; + GTM_LOG_BACKGROUND_SESSION(@"User Info from session identifier: %@ %@", + metadataDict, error ? error : @""); + return metadataDict; + } + return nil; +} + +- (NSString *)createSessionIdentifierWithMetadata:(NSDictionary * GTM_NULLABLE_TYPE)metadataToInclude { + NSString *result; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Session Identifier format: "com.google.<ClassName>_<UUID>_<Metadata in JSON format> + GTMSESSION_ASSERT_DEBUG(!_sessionIdentifier, @"Session identifier already created"); + _sessionIdentifierUUID = [[NSUUID UUID] UUIDString]; + _sessionIdentifier = + [NSString stringWithFormat:@"%@_%@", kGTMSessionIdentifierPrefix, _sessionIdentifierUUID]; + // Start with user-supplied keys so they cannot accidentally override the fetcher's keys. + NSMutableDictionary *metadataDict = + [NSMutableDictionary dictionaryWithDictionary:(NSDictionary * GTM_NONNULL_TYPE)_sessionUserInfo]; + + if (metadataToInclude) { + [metadataDict addEntriesFromDictionary:(NSDictionary *)metadataToInclude]; + } + NSDictionary *defaultMetadataDict = [self sessionIdentifierDefaultMetadata]; + if (defaultMetadataDict) { + [metadataDict addEntriesFromDictionary:defaultMetadataDict]; + } + if (metadataDict.count > 0) { + NSData *metadataData = [NSJSONSerialization dataWithJSONObject:metadataDict + options:0 + error:NULL]; + GTMSESSION_ASSERT_DEBUG(metadataData != nil, + @"Session identifier user info failed to convert to JSON"); + if (metadataData.length > 0) { + NSString *metadataString = [[NSString alloc] initWithData:metadataData + encoding:NSUTF8StringEncoding]; + _sessionIdentifier = + [_sessionIdentifier stringByAppendingFormat:@"_%@", metadataString]; + } + } + _didCreateSessionIdentifier = YES; + result = _sessionIdentifier; + } // @synchronized(self) + return result; +} + +- (void)failToBeginFetchWithError:(NSError *)error { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _hasStoppedFetching = YES; + } + + if (error == nil) { + error = [NSError errorWithDomain:kGTMSessionFetcherErrorDomain + code:GTMSessionFetcherErrorDownloadFailed + userInfo:nil]; + } + + [self invokeFetchCallbacksOnCallbackQueueWithData:nil + error:error]; + [self releaseCallbacks]; + + [_service fetcherDidStop:self]; + + self.authorizer = nil; +} + ++ (GTMSessionCookieStorage *)staticCookieStorage { + static GTMSessionCookieStorage *gCookieStorage = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gCookieStorage = [[GTMSessionCookieStorage alloc] init]; + }); + return gCookieStorage; +} + +#if GTM_BACKGROUND_TASK_FETCHING + +- (void)endBackgroundTask { + // Whenever the connection stops or background execution expires, + // we need to tell UIApplication we're done. + UIBackgroundTaskIdentifier bgTaskID; + @synchronized(self) { + bgTaskID = self.backgroundTaskIdentifier; + if (bgTaskID != UIBackgroundTaskInvalid) { + self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + } + + if (bgTaskID != UIBackgroundTaskInvalid) { + id<GTMUIApplicationProtocol> app = [[self class] fetcherUIApplication]; + [app endBackgroundTask:bgTaskID]; + } +} + +#endif // GTM_BACKGROUND_TASK_FETCHING + +- (void)authorizeRequest { + GTMSessionCheckNotSynchronized(self); + + id authorizer = self.authorizer; + SEL asyncAuthSel = @selector(authorizeRequest:delegate:didFinishSelector:); + if ([authorizer respondsToSelector:asyncAuthSel]) { + SEL callbackSel = @selector(authorizer:request:finishedWithError:); + NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; + [authorizer authorizeRequest:mutableRequest + delegate:self + didFinishSelector:callbackSel]; + } else { + GTMSESSION_ASSERT_DEBUG(authorizer == nil, @"invalid authorizer for fetch"); + + // No authorizing possible, and authorizing happens only after any delay; + // just begin fetching + [self beginFetchMayDelay:NO + mayAuthorize:NO]; + } +} + +- (void)authorizer:(id<GTMFetcherAuthorizationProtocol>)auth + request:(NSMutableURLRequest *)authorizedRequest + finishedWithError:(NSError *)error { + GTMSessionCheckNotSynchronized(self); + + if (error != nil) { + // We can't fetch without authorization + [self failToBeginFetchWithError:error]; + } else { + @synchronized(self) { + _request = authorizedRequest; + } + [self beginFetchMayDelay:NO + mayAuthorize:NO]; + } +} + + +- (BOOL)canFetchWithBackgroundSession { + // Subclasses may override. + return YES; +} + +// Returns YES if the fetcher has been started and has not yet stopped. +// +// Fetching includes waiting for authorization or for retry, waiting to be allowed by the +// service object to start the request, and actually fetching the request. +- (BOOL)isFetching { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [self isFetchingUnsynchronized]; + } +} + +- (BOOL)isFetchingUnsynchronized { + GTMSessionCheckSynchronized(self); + + BOOL hasBegun = (_initialBeginFetchDate != nil); + return hasBegun && !_hasStoppedFetching; +} + +- (NSURLResponse * GTM_NULLABLE_TYPE)response { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSURLResponse *response = [self responseUnsynchronized]; + return response; + } // @synchronized(self) +} + +- (NSURLResponse * GTM_NULLABLE_TYPE)responseUnsynchronized { + GTMSessionCheckSynchronized(self); + + NSURLResponse *response = _sessionTask.response; + if (!response) response = _response; + return response; +} + +- (NSInteger)statusCode { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSInteger statusCode = [self statusCodeUnsynchronized]; + return statusCode; + } // @synchronized(self) +} + +- (NSInteger)statusCodeUnsynchronized { + GTMSessionCheckSynchronized(self); + + NSURLResponse *response = [self responseUnsynchronized]; + NSInteger statusCode; + + if ([response respondsToSelector:@selector(statusCode)]) { + statusCode = [(NSHTTPURLResponse *)response statusCode]; + } else { + // Default to zero, in hopes of hinting "Unknown" (we can't be + // sure that things are OK enough to use 200). + statusCode = 0; + } + return statusCode; +} + +- (NSDictionary * GTM_NULLABLE_TYPE)responseHeaders { + GTMSessionCheckNotSynchronized(self); + + NSURLResponse *response = self.response; + if ([response respondsToSelector:@selector(allHeaderFields)]) { + NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; + return headers; + } + return nil; +} + +- (NSDictionary * GTM_NULLABLE_TYPE)responseHeadersUnsynchronized { + GTMSessionCheckSynchronized(self); + + NSURLResponse *response = [self responseUnsynchronized]; + if ([response respondsToSelector:@selector(allHeaderFields)]) { + NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; + return headers; + } + return nil; +} + +- (void)releaseCallbacks { + // Avoid releasing blocks in the sync section since objects dealloc'd by + // the blocks being released may call back into the fetcher or fetcher + // service. + dispatch_queue_t NS_VALID_UNTIL_END_OF_SCOPE holdCallbackQueue; + GTMSessionFetcherCompletionHandler NS_VALID_UNTIL_END_OF_SCOPE holdCompletionHandler; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + holdCallbackQueue = _callbackQueue; + holdCompletionHandler = _completionHandler; + + _callbackQueue = nil; + _completionHandler = nil; // Setter overridden in upload. Setter assumed to be used externally. + } + + // Set local callback pointers to nil here rather than let them release at the end of the scope + // to make any problems due to the blocks being released be a bit more obvious in a stack trace. + holdCallbackQueue = nil; + holdCompletionHandler = nil; + + self.configurationBlock = nil; + self.didReceiveResponseBlock = nil; + self.challengeBlock = nil; + self.willRedirectBlock = nil; + self.sendProgressBlock = nil; + self.receivedProgressBlock = nil; + self.downloadProgressBlock = nil; + self.accumulateDataBlock = nil; + self.willCacheURLResponseBlock = nil; + self.retryBlock = nil; + self.testBlock = nil; + self.resumeDataBlock = nil; +} + +- (void)forgetSessionIdentifierForFetcher { + GTMSessionCheckSynchronized(self); + [self forgetSessionIdentifierForFetcherWithoutSyncCheck]; +} + +- (void)forgetSessionIdentifierForFetcherWithoutSyncCheck { + // This should be called inside a @synchronized block (except during dealloc.) + if (_sessionIdentifier) { + NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIdentifierToFetcherMap]; + [sessionIdentifierToFetcherMap removeObjectForKey:_sessionIdentifier]; + _sessionIdentifier = nil; + _didCreateSessionIdentifier = NO; + } +} + +// External stop method +- (void)stopFetching { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Prevent enqueued callbacks from executing. + _userStoppedFetching = YES; + } // @synchronized(self) + [self stopFetchReleasingCallbacks:YES]; +} + +// Cancel the fetch of the URL that's currently in progress. +// +// If shouldReleaseCallbacks is NO then the fetch will be retried so the callbacks +// need to still be retained. +- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks { + [self removePersistedBackgroundSessionFromDefaults]; + + id<GTMSessionFetcherServiceProtocol> service; + NSMutableURLRequest *request; + + // If the task or the retry timer is all that's retaining the fetcher, + // we want to be sure this instance survives stopping at least long enough for + // the stack to unwind. + __autoreleasing GTMSessionFetcher *holdSelf = self; + + BOOL hasCanceledTask = NO; + + [holdSelf destroyRetryTimer]; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _hasStoppedFetching = YES; + + service = _service; + request = _request; + + if (_sessionTask) { + // In case cancelling the task or session calls this recursively, we want + // to ensure that we'll only release the task and delegate once, + // so first set _sessionTask to nil + // + // This may be called in a callback from the task, so use autorelease to avoid + // releasing the task in its own callback. + __autoreleasing NSURLSessionTask *oldTask = _sessionTask; + if (!_isUsingTestBlock) { + _response = _sessionTask.response; + } + _sessionTask = nil; + + if ([oldTask state] != NSURLSessionTaskStateCompleted) { + // For download tasks, when the fetch is stopped, we may provide resume data that can + // be used to create a new session. + BOOL mayResume = (_resumeDataBlock + && [oldTask respondsToSelector:@selector(cancelByProducingResumeData:)]); + if (!mayResume) { + [oldTask cancel]; + // A side effect of stopping the task is that URLSession:task:didCompleteWithError: + // will be invoked asynchronously on the delegate queue. + } else { + void (^resumeBlock)(NSData *) = _resumeDataBlock; + _resumeDataBlock = nil; + + // Save callbackQueue since releaseCallbacks clears it. + dispatch_queue_t callbackQueue = _callbackQueue; + dispatch_group_enter(_callbackGroup); + [(NSURLSessionDownloadTask *)oldTask cancelByProducingResumeData:^(NSData *resumeData) { + [self invokeOnCallbackQueue:callbackQueue + afterUserStopped:YES + block:^{ + resumeBlock(resumeData); + dispatch_group_leave(self->_callbackGroup); + }]; + }]; + } + hasCanceledTask = YES; + } + } + + // If the task was canceled, wait until the URLSession:task:didCompleteWithError: to call + // finishTasksAndInvalidate, since calling it immediately tends to crash, see radar 18471901. + if (_session) { + BOOL shouldInvalidate = _shouldInvalidateSession; +#if TARGET_OS_IPHONE + // Don't invalidate if we've got a systemCompletionHandler, since + // URLSessionDidFinishEventsForBackgroundURLSession: won't be called if invalidated. + shouldInvalidate = shouldInvalidate && !self.systemCompletionHandler; +#endif + if (shouldInvalidate) { + __autoreleasing NSURLSession *oldSession = _session; + _session = nil; + + if (!hasCanceledTask) { + [oldSession finishTasksAndInvalidate]; + } else { + _sessionNeedingInvalidation = oldSession; + } + } + } + } // @synchronized(self) + + // send the stopped notification + [self sendStopNotificationIfNeeded]; + + [_authorizer stopAuthorizationForRequest:request]; + + if (shouldReleaseCallbacks) { + [self releaseCallbacks]; + + self.authorizer = nil; + } + + [service fetcherDidStop:self]; + +#if GTM_BACKGROUND_TASK_FETCHING + [self endBackgroundTask]; +#endif +} + +- (void)setStopNotificationNeeded:(BOOL)flag { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _isStopNotificationNeeded = flag; + } // @synchronized(self) +} + +- (void)sendStopNotificationIfNeeded { + BOOL sendNow = NO; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_isStopNotificationNeeded) { + _isStopNotificationNeeded = NO; + sendNow = YES; + } + } // @synchronized(self) + + if (sendNow) { + [self postNotificationOnMainThreadWithName:kGTMSessionFetcherStoppedNotification + userInfo:nil + requireAsync:NO]; + } +} + +- (void)retryFetch { + [self stopFetchReleasingCallbacks:NO]; + + // A retry will need a configuration with a fresh session identifier. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_sessionIdentifier && _didCreateSessionIdentifier) { + [self forgetSessionIdentifierForFetcher]; + _configuration = nil; + } + + if (_canShareSession) { + // Force a grab of the current session from the fetcher service in case + // the service's old one has become invalid. + _session = nil; + } + } // @synchronized(self) + + [self beginFetchForRetry]; +} + +- (BOOL)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds { + // Uncovered in upload fetcher testing, because the chunk fetcher is being waited on, and gets + // released by the upload code. The uploader just holds onto it with an ivar, and that gets + // nilled in the chunk fetcher callback. + // Used once in while loop just to avoid unused variable compiler warning. + __autoreleasing GTMSessionFetcher *holdSelf = self; + + NSDate *giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + + BOOL shouldSpinRunLoop = ([NSThread isMainThread] && + (!self.callbackQueue + || self.callbackQueue == dispatch_get_main_queue())); + BOOL expired = NO; + + // Loop until the callbacks have been called and released, and until + // the connection is no longer pending, until there are no callback dispatches + // in flight, or until the timeout has expired. + int64_t delta = (int64_t)(100 * NSEC_PER_MSEC); // 100 ms + while (1) { + BOOL isTaskInProgress = (holdSelf->_sessionTask + && [_sessionTask state] != NSURLSessionTaskStateCompleted); + BOOL needsToCallCompletion = (_completionHandler != nil); + BOOL isCallbackInProgress = (_callbackGroup + && dispatch_group_wait(_callbackGroup, dispatch_time(DISPATCH_TIME_NOW, delta))); + + if (!isTaskInProgress && !needsToCallCompletion && !isCallbackInProgress) break; + + expired = ([giveUpDate timeIntervalSinceNow] < 0); + if (expired) { + GTMSESSION_LOG_DEBUG(@"GTMSessionFetcher waitForCompletionWithTimeout:%0.1f expired -- " + @"%@%@%@", timeoutInSeconds, + isTaskInProgress ? @"taskInProgress " : @"", + needsToCallCompletion ? @"needsToCallCompletion " : @"", + isCallbackInProgress ? @"isCallbackInProgress" : @""); + break; + } + + // Run the current run loop 1/1000 of a second to give the networking + // code a chance to work + const NSTimeInterval kSpinInterval = 0.001; + if (shouldSpinRunLoop) { + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:kSpinInterval]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } else { + [NSThread sleepForTimeInterval:kSpinInterval]; + } + } + return !expired; +} + ++ (void)setGlobalTestBlock:(GTMSessionFetcherTestBlock GTM_NULLABLE_TYPE)block { +#if GTM_DISABLE_FETCHER_TEST_BLOCK + GTMSESSION_ASSERT_DEBUG(block == nil, @"test blocks disabled"); +#endif + gGlobalTestBlock = [block copy]; +} + +#if GTM_BACKGROUND_TASK_FETCHING + +static GTM_NULLABLE_TYPE id<GTMUIApplicationProtocol> gSubstituteUIApp; + ++ (void)setSubstituteUIApplication:(nullable id<GTMUIApplicationProtocol>)app { + gSubstituteUIApp = app; +} + ++ (nullable id<GTMUIApplicationProtocol>)substituteUIApplication { + return gSubstituteUIApp; +} + ++ (nullable id<GTMUIApplicationProtocol>)fetcherUIApplication { + id<GTMUIApplicationProtocol> app = gSubstituteUIApp; + if (app) return app; + + // iOS App extensions should not call [UIApplication sharedApplication], even + // if UIApplication responds to it. + + static Class applicationClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + BOOL isAppExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]; + if (!isAppExtension) { + Class cls = NSClassFromString(@"UIApplication"); + if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) { + applicationClass = cls; + } + } + }); + + if (applicationClass) { + app = (id<GTMUIApplicationProtocol>)[applicationClass sharedApplication]; + } + return app; +} +#endif // GTM_BACKGROUND_TASK_FETCHING + +#pragma mark NSURLSession Delegate Methods + +// NSURLSession documentation indicates that redirectRequest can be passed to the handler +// but empirically redirectRequest lacks the HTTP body, so passing it will break POSTs. +// Instead, we construct a new request, a copy of the original, with overrides from the +// redirect. + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)redirectResponse + newRequest:(NSURLRequest *)redirectRequest + completionHandler:(void (^)(NSURLRequest * GTM_NULLABLE_TYPE))handler { + [self setSessionTask:task]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ task:%@ willPerformHTTPRedirection:%@ newRequest:%@", + [self class], self, session, task, redirectResponse, redirectRequest); + + if ([self userStoppedFetching]) { + handler(nil); + return; + } + if (redirectRequest && redirectResponse) { + // Copy the original request, including the body. + NSURLRequest *originalRequest = self.request; + NSMutableURLRequest *newRequest = [originalRequest mutableCopy]; + + // The new requests's URL overrides the original's URL. + [newRequest setURL:[GTMSessionFetcher redirectURLWithOriginalRequestURL:originalRequest.URL + redirectRequestURL:redirectRequest.URL]]; + + // Any headers in the redirect override headers in the original. + NSDictionary *redirectHeaders = redirectRequest.allHTTPHeaderFields; + for (NSString *key in redirectHeaders) { + NSString *value = [redirectHeaders objectForKey:key]; + [newRequest setValue:value forHTTPHeaderField:key]; + } + + redirectRequest = newRequest; + + // Log the response we just received + [self setResponse:redirectResponse]; + [self logNowWithError:nil]; + + GTMSessionFetcherWillRedirectBlock willRedirectBlock = self.willRedirectBlock; + if (willRedirectBlock) { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + [self invokeOnCallbackQueueAfterUserStopped:YES + block:^{ + willRedirectBlock(redirectResponse, redirectRequest, ^(NSURLRequest *clientRequest) { + + // Update the request for future logging. + [self updateMutableRequest:[clientRequest mutableCopy]]; + + handler(clientRequest); + }); + }]; + } // @synchronized(self) + return; + } + // Continues here if the client did not provide a redirect block. + + // Update the request for future logging. + [self updateMutableRequest:[redirectRequest mutableCopy]]; + } + handler(redirectRequest); +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))handler { + [self setSessionTask:dataTask]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ dataTask:%@ didReceiveResponse:%@", + [self class], self, session, dataTask, response); + void (^accumulateAndFinish)(NSURLSessionResponseDisposition) = + ^(NSURLSessionResponseDisposition dispositionValue) { + // This method is called when the server has determined that it + // has enough information to create the NSURLResponse + // it can be called multiple times, for example in the case of a + // redirect, so each time we reset the data. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + BOOL hadPreviousData = self->_downloadedLength > 0; + + [self->_downloadedData setLength:0]; + self->_downloadedLength = 0; + + if (hadPreviousData && (dispositionValue != NSURLSessionResponseCancel)) { + // Tell the accumulate block to discard prior data. + GTMSessionFetcherAccumulateDataBlock accumulateBlock = self->_accumulateDataBlock; + if (accumulateBlock) { + [self invokeOnCallbackQueueUnlessStopped:^{ + accumulateBlock(nil); + }]; + } + } + } // @synchronized(self) + handler(dispositionValue); + }; + + GTMSessionFetcherDidReceiveResponseBlock receivedResponseBlock; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + receivedResponseBlock = _didReceiveResponseBlock; + if (receivedResponseBlock) { + // We will ultimately need to call back to NSURLSession's handler with the disposition value + // for this delegate method even if the user has stopped the fetcher. + [self invokeOnCallbackQueueAfterUserStopped:YES + block:^{ + receivedResponseBlock(response, ^(NSURLSessionResponseDisposition desiredDisposition) { + accumulateAndFinish(desiredDisposition); + }); + }]; + } + } // @synchronized(self) + + if (receivedResponseBlock == nil) { + accumulateAndFinish(NSURLSessionResponseAllow); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ dataTask:%@ didBecomeDownloadTask:%@", + [self class], self, session, dataTask, downloadTask); + [self setSessionTask:downloadTask]; +} + + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * GTM_NULLABLE_TYPE credential))handler { + [self setSessionTask:task]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ task:%@ didReceiveChallenge:%@", + [self class], self, session, task, challenge); + + GTMSessionFetcherChallengeBlock challengeBlock = self.challengeBlock; + if (challengeBlock) { + // The fetcher user has provided custom challenge handling. + // + // We will ultimately need to call back to NSURLSession's handler with the disposition value + // for this delegate method even if the user has stopped the fetcher. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self invokeOnCallbackQueueAfterUserStopped:YES + block:^{ + challengeBlock(self, challenge, handler); + }]; + } + } else { + // No challenge block was provided by the client. + [self respondToChallenge:challenge + completionHandler:handler]; + } +} + +- (void)respondToChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * GTM_NULLABLE_TYPE credential))handler { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSInteger previousFailureCount = [challenge previousFailureCount]; + if (previousFailureCount <= 2) { + NSURLProtectionSpace *protectionSpace = [challenge protectionSpace]; + NSString *authenticationMethod = [protectionSpace authenticationMethod]; + if ([authenticationMethod isEqual:NSURLAuthenticationMethodServerTrust]) { + // SSL. + // + // Background sessions seem to require an explicit check of the server trust object + // rather than default handling. + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + if (serverTrust == NULL) { + // No server trust information is available. + handler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } else { + // Server trust information is available. + void (^callback)(SecTrustRef, BOOL) = ^(SecTrustRef trustRef, BOOL allow){ + if (allow) { + NSURLCredential *trustCredential = [NSURLCredential credentialForTrust:trustRef]; + handler(NSURLSessionAuthChallengeUseCredential, trustCredential); + } else { + GTMSESSION_LOG_DEBUG(@"Cancelling authentication challenge for %@", self->_request.URL); + handler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); + } + }; + if (_allowInvalidServerCertificates) { + callback(serverTrust, YES); + } else { + [[self class] evaluateServerTrust:serverTrust + forRequest:_request + completionHandler:callback]; + } + } + return; + } + + NSURLCredential *credential = _credential; + + if ([[challenge protectionSpace] isProxy] && _proxyCredential != nil) { + credential = _proxyCredential; + } + + if (credential) { + handler(NSURLSessionAuthChallengeUseCredential, credential); + } else { + // The credential is still nil; tell the OS to use the default handling. This is needed + // for things that can come out of the keychain (proxies, client certificates, etc.). + // + // Note: Looking up a credential with NSURLCredentialStorage's + // defaultCredentialForProtectionSpace: is *not* the same invoking the handler with + // NSURLSessionAuthChallengePerformDefaultHandling. In the case of + // NSURLAuthenticationMethodClientCertificate, you can get nil back from + // NSURLCredentialStorage, while using this code path instead works. + handler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + + } else { + // We've failed auth 3 times. The completion handler will be called with code + // NSURLErrorCancelled. + handler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); + } + } // @synchronized(self) +} + +// Return redirect URL based on the original request URL and redirect request URL. +// +// Method disallows any scheme changes between the original request URL and redirect request URL +// aside from "http" to "https". If a change in scheme is detected the redirect URL inherits the +// scheme from the original request URL. ++ (GTM_NULLABLE NSURL *)redirectURLWithOriginalRequestURL:(GTM_NULLABLE NSURL *)originalRequestURL + redirectRequestURL:(GTM_NULLABLE NSURL *)redirectRequestURL { + // In the case of an NSURLSession redirect, neither URL should ever be nil; as a sanity check + // if either is nil return the other URL. + if (!redirectRequestURL) return originalRequestURL; + if (!originalRequestURL) return redirectRequestURL; + + NSString *originalScheme = originalRequestURL.scheme; + NSString *redirectScheme = redirectRequestURL.scheme; + BOOL insecureToSecureRedirect = + (originalScheme != nil && [originalScheme caseInsensitiveCompare:@"http"] == NSOrderedSame && + redirectScheme != nil && [redirectScheme caseInsensitiveCompare:@"https"] == NSOrderedSame); + + // Check for changes to the scheme and disallow any changes except for http to https. + if (!insecureToSecureRedirect && + (redirectScheme.length != originalScheme.length || + [redirectScheme caseInsensitiveCompare:originalScheme] != NSOrderedSame)) { + NSURLComponents *components = + [NSURLComponents componentsWithURL:(NSURL * _Nonnull)redirectRequestURL + resolvingAgainstBaseURL:NO]; + components.scheme = originalScheme; + return components.URL; + } + + return redirectRequestURL; +} + +// Validate the certificate chain. +// +// This may become a public method if it appears to be useful to users. ++ (void)evaluateServerTrust:(SecTrustRef)serverTrust + forRequest:(NSURLRequest *)request + completionHandler:(void (^)(SecTrustRef trustRef, BOOL allow))handler { + // Retain the trust object to avoid a SecTrustEvaluate() crash on iOS 7. + CFRetain(serverTrust); + + // Evaluate the certificate chain. + // + // The delegate queue may be the main thread. Trust evaluation could cause some + // blocking network activity, so we must evaluate async, as documented at + // https://developer.apple.com/library/ios/technotes/tn2232/ + // + // We must also avoid multiple uses of the trust object, per docs: + // "It is not safe to call this function concurrently with any other function that uses + // the same trust management object, or to re-enter this function for the same trust + // management object." + // + // SecTrustEvaluateAsync both does sync execution of Evaluate and calls back on the + // queue passed to it, according to at sources in + // http://www.opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55050.9/lib/SecTrust.cpp + // It would require a global serial queue to ensure the evaluate happens only on a + // single thread at a time, so we'll stick with using SecTrustEvaluate on a background + // thread. + dispatch_queue_t evaluateBackgroundQueue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(evaluateBackgroundQueue, ^{ + // It looks like the implementation of SecTrustEvaluate() on Mac grabs a global lock, + // so it may be redundant for us to also lock, but it's easy to synchronize here + // anyway. + SecTrustResultType trustEval = kSecTrustResultInvalid; + BOOL shouldAllow; + OSStatus trustError; + @synchronized([GTMSessionFetcher class]) { + GTMSessionMonitorSynchronized([GTMSessionFetcher class]); + + trustError = SecTrustEvaluate(serverTrust, &trustEval); + } + if (trustError != errSecSuccess) { + GTMSESSION_LOG_DEBUG(@"Error %d evaluating trust for %@", + (int)trustError, request); + shouldAllow = NO; + } else { + // Having a trust level "unspecified" by the user is the usual result, described at + // https://developer.apple.com/library/mac/qa/qa1360 + if (trustEval == kSecTrustResultUnspecified + || trustEval == kSecTrustResultProceed) { + shouldAllow = YES; + } else { + shouldAllow = NO; + GTMSESSION_LOG_DEBUG(@"Challenge SecTrustResultType %u for %@, properties: %@", + trustEval, request.URL.host, + CFBridgingRelease(SecTrustCopyProperties(serverTrust))); + } + } + handler(serverTrust, shouldAllow); + + CFRelease(serverTrust); + }); +} + +- (void)invokeOnCallbackQueueUnlessStopped:(void (^)(void))block { + [self invokeOnCallbackQueueAfterUserStopped:NO + block:block]; +} + +- (void)invokeOnCallbackQueueAfterUserStopped:(BOOL)afterStopped + block:(void (^)(void))block { + GTMSessionCheckSynchronized(self); + + [self invokeOnCallbackUnsynchronizedQueueAfterUserStopped:afterStopped + block:block]; +} + +- (void)invokeOnCallbackUnsynchronizedQueueAfterUserStopped:(BOOL)afterStopped + block:(void (^)(void))block { + // testBlock simulation code may not be synchronizing when this is invoked. + [self invokeOnCallbackQueue:_callbackQueue + afterUserStopped:afterStopped + block:block]; +} + +- (void)invokeOnCallbackQueue:(dispatch_queue_t)callbackQueue + afterUserStopped:(BOOL)afterStopped + block:(void (^)(void))block { + if (callbackQueue) { + dispatch_group_async(_callbackGroup, callbackQueue, ^{ + if (!afterStopped) { + NSDate *serviceStoppedAllDate = [self->_service stoppedAllFetchersDate]; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Avoid a race between stopFetching and the callback. + if (self->_userStoppedFetching) { + return; + } + + // Also avoid calling back if the service has stopped all fetchers + // since this one was created. The fetcher may have stopped before + // stopAllFetchers was invoked, so _userStoppedFetching wasn't set, + // but the app still won't expect the callback to fire after + // the service's stopAllFetchers was invoked. + if (serviceStoppedAllDate + && [self->_initialBeginFetchDate compare:serviceStoppedAllDate] != NSOrderedDescending) { + // stopAllFetchers was called after this fetcher began. + return; + } + } // @synchronized(self) + } + block(); + }); + } +} + +- (void)invokeFetchCallbacksOnCallbackQueueWithData:(GTM_NULLABLE NSData *)data + error:(GTM_NULLABLE NSError *)error { + // Callbacks will be released in the method stopFetchReleasingCallbacks: + GTMSessionFetcherCompletionHandler handler; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + handler = _completionHandler; + + if (handler) { + [self invokeOnCallbackQueueUnlessStopped:^{ + handler(data, error); + + // Post a notification, primarily to allow code to collect responses for + // testing. + // + // The observing code is not likely on the fetcher's callback + // queue, so this posts explicitly to the main queue. + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (data) { + userInfo[kGTMSessionFetcherCompletionDataKey] = data; + } + if (error) { + userInfo[kGTMSessionFetcherCompletionErrorKey] = error; + } + [self postNotificationOnMainThreadWithName:kGTMSessionFetcherCompletionInvokedNotification + userInfo:userInfo + requireAsync:NO]; + }]; + } + } // @synchronized(self) +} + +- (void)postNotificationOnMainThreadWithName:(NSString *)noteName + userInfo:(GTM_NULLABLE NSDictionary *)userInfo + requireAsync:(BOOL)requireAsync { + dispatch_block_t postBlock = ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:noteName + object:self + userInfo:userInfo]; + }; + + if ([NSThread isMainThread] && !requireAsync) { + // Post synchronously for compatibility with older code using the fetcher. + + // Avoid calling out to other code from inside a sync block to avoid risk + // of a deadlock or of recursive sync. + GTMSessionCheckNotSynchronized(self); + + postBlock(); + } else { + dispatch_async(dispatch_get_main_queue(), postBlock); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)uploadTask + needNewBodyStream:(void (^)(NSInputStream * GTM_NULLABLE_TYPE bodyStream))completionHandler { + [self setSessionTask:uploadTask]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ task:%@ needNewBodyStream:", + [self class], self, session, uploadTask); + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSessionFetcherBodyStreamProvider provider = _bodyStreamProvider; +#if !STRIP_GTM_FETCH_LOGGING + if ([self respondsToSelector:@selector(loggedStreamProviderForStreamProvider:)]) { + provider = [self performSelector:@selector(loggedStreamProviderForStreamProvider:) + withObject:provider]; + } +#endif + if (provider) { + [self invokeOnCallbackQueueUnlessStopped:^{ + provider(completionHandler); + }]; + } else { + GTMSESSION_ASSERT_DEBUG(NO, @"NSURLSession expects a stream provider"); + + completionHandler(nil); + } + } // @synchronized(self) +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + [self setSessionTask:task]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ task:%@ didSendBodyData:%lld" + @" totalBytesSent:%lld totalBytesExpectedToSend:%lld", + [self class], self, session, task, bytesSent, totalBytesSent, + totalBytesExpectedToSend); + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (!_sendProgressBlock) { + return; + } + // We won't hold on to send progress block; it's ok to not send it if the upload finishes. + [self invokeOnCallbackQueueUnlessStopped:^{ + GTMSessionFetcherSendProgressBlock progressBlock; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + progressBlock = self->_sendProgressBlock; + } + if (progressBlock) { + progressBlock(bytesSent, totalBytesSent, totalBytesExpectedToSend); + } + }]; + } // @synchronized(self) +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data { + [self setSessionTask:dataTask]; + NSUInteger bufferLength = data.length; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ dataTask:%@ didReceiveData:%p (%llu bytes)", + [self class], self, session, dataTask, data, + (unsigned long long)bufferLength); + if (bufferLength == 0) { + // Observed on completing an out-of-process upload. + return; + } + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSessionFetcherAccumulateDataBlock accumulateBlock = _accumulateDataBlock; + if (accumulateBlock) { + // Let the client accumulate the data. + _downloadedLength += bufferLength; + [self invokeOnCallbackQueueUnlessStopped:^{ + accumulateBlock(data); + }]; + } else if (!_userStoppedFetching) { + // Append to the mutable data buffer unless the fetch has been cancelled. + + // Resumed upload tasks may not yet have a data buffer. + if (_downloadedData == nil) { + // Using NSClassFromString for iOS 6 compatibility. + GTMSESSION_ASSERT_DEBUG( + ![dataTask isKindOfClass:NSClassFromString(@"NSURLSessionDownloadTask")], + @"Resumed download tasks should not receive data bytes"); + _downloadedData = [[NSMutableData alloc] init]; + } + + [_downloadedData appendData:data]; + _downloadedLength = (int64_t)_downloadedData.length; + + // We won't hold on to receivedProgressBlock here; it's ok to not send + // it if the transfer finishes. + if (_receivedProgressBlock) { + [self invokeOnCallbackQueueUnlessStopped:^{ + GTMSessionFetcherReceivedProgressBlock progressBlock; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + progressBlock = self->_receivedProgressBlock; + } + if (progressBlock) { + progressBlock((int64_t)bufferLength, self->_downloadedLength); + } + }]; + } + } + } // @synchronized(self) +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ dataTask:%@ willCacheResponse:%@ %@", + [self class], self, session, dataTask, + proposedResponse, proposedResponse.response); + GTMSessionFetcherWillCacheURLResponseBlock callback; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + callback = _willCacheURLResponseBlock; + + if (callback) { + [self invokeOnCallbackQueueAfterUserStopped:YES + block:^{ + callback(proposedResponse, completionHandler); + }]; + } + } // @synchronized(self) + if (!callback) { + completionHandler(proposedResponse); + } +} + + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ downloadTask:%@ didWriteData:%lld" + @" bytesWritten:%lld totalBytesExpectedToWrite:%lld", + [self class], self, session, downloadTask, bytesWritten, + totalBytesWritten, totalBytesExpectedToWrite); + [self setSessionTask:downloadTask]; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if ((totalBytesExpectedToWrite != NSURLSessionTransferSizeUnknown) && + (totalBytesExpectedToWrite < totalBytesWritten)) { + // Have observed cases were bytesWritten == totalBytesExpectedToWrite, + // but totalBytesWritten > totalBytesExpectedToWrite, so setting to unkown in these cases. + totalBytesExpectedToWrite = NSURLSessionTransferSizeUnknown; + } + // We won't hold on to download progress block during the enqueue; + // it's ok to not send it if the upload finishes. + + [self invokeOnCallbackQueueUnlessStopped:^{ + GTMSessionFetcherDownloadProgressBlock progressBlock; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + progressBlock = self->_downloadProgressBlock; + } + if (progressBlock) { + progressBlock(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } + }]; + } // @synchronized(self) +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ downloadTask:%@ didResumeAtOffset:%lld" + @" expectedTotalBytes:%lld", + [self class], self, session, downloadTask, fileOffset, + expectedTotalBytes); + [self setSessionTask:downloadTask]; +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)downloadLocationURL { + // Download may have relaunched app, so update _sessionTask. + [self setSessionTask:downloadTask]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ downloadTask:%@ didFinishDownloadingToURL:%@", + [self class], self, session, downloadTask, downloadLocationURL); + NSNumber *fileSizeNum; + [downloadLocationURL getResourceValue:&fileSizeNum + forKey:NSURLFileSizeKey + error:NULL]; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSURL *destinationURL = _destinationFileURL; + + _downloadedLength = fileSizeNum.longLongValue; + + // Overwrite any previous file at the destination URL. + NSFileManager *fileMgr = [NSFileManager defaultManager]; + NSError *removeError; + if (![fileMgr removeItemAtURL:destinationURL error:&removeError] + && removeError.code != NSFileNoSuchFileError) { + GTMSESSION_LOG_DEBUG(@"Could not remove previous file at %@ due to %@", + downloadLocationURL.path, removeError); + } + + NSInteger statusCode = [self statusCodeUnsynchronized]; + if (statusCode < 200 || statusCode > 399) { + // In OS X 10.11, the response body is written to a file even on a server + // status error. For convenience of the fetcher client, we'll skip saving the + // downloaded body to the destination URL so that clients do not need to know + // to delete the file following fetch errors. + GTMSESSION_LOG_DEBUG(@"Abandoning download due to status %ld, file %@", + (long)statusCode, downloadLocationURL.path); + + // On error code, add the contents of the temporary file to _downloadTaskErrorData + // This way fetcher clients have access to error details possibly passed by the server. + if (_downloadedLength > 0 && _downloadedLength <= kMaximumDownloadErrorDataLength) { + _downloadTaskErrorData = [NSData dataWithContentsOfURL:downloadLocationURL]; + } else if (_downloadedLength > kMaximumDownloadErrorDataLength) { + GTMSESSION_LOG_DEBUG(@"Download error data for file %@ not passed to userInfo due to size " + @"%lld", downloadLocationURL.path, _downloadedLength); + } + } else { + NSError *moveError; + NSURL *destinationFolderURL = [destinationURL URLByDeletingLastPathComponent]; + BOOL didMoveDownload = NO; + if ([fileMgr createDirectoryAtURL:destinationFolderURL + withIntermediateDirectories:YES + attributes:nil + error:&moveError]) { + didMoveDownload = [fileMgr moveItemAtURL:downloadLocationURL + toURL:destinationURL + error:&moveError]; + } + if (!didMoveDownload) { + _downloadFinishedError = moveError; + } + GTM_LOG_BACKGROUND_SESSION(@"%@ %p Moved download from \"%@\" to \"%@\" %@", + [self class], self, + downloadLocationURL.path, destinationURL.path, + error ? error : @""); + } + } // @synchronized(self) +} + +/* Sent as the last message related to a specific task. Error may be + * nil, which implies that no error occurred and this task is complete. + */ +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error { + [self setSessionTask:task]; + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ task:%@ didCompleteWithError:%@", + [self class], self, session, task, error); + + NSInteger status = self.statusCode; + BOOL forceAssumeRetry = NO; + BOOL succeeded = NO; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + +#if !GTM_DISABLE_FETCHER_TEST_BLOCK + // The task is never resumed when a testBlock is used. When the session is destroyed, + // we should ignore the callback, since the testBlock support code itself invokes + // shouldRetryNowForStatus: and finishWithError:shouldRetry: + if (_isUsingTestBlock) return; +#endif + + if (error == nil) { + error = _downloadFinishedError; + } + succeeded = (error == nil && status >= 0 && status < 300); + if (succeeded) { + // Succeeded. + _bodyLength = task.countOfBytesSent; + } + } // @synchronized(self) + + if (succeeded) { + [self finishWithError:nil shouldRetry:NO]; + return; + } + // For background redirects, no delegate method is called, so we cannot restore a stripped + // Authorization header, so if a 403 ("Forbidden") was generated due to a missing OAuth 2 header, + // set the current request's URL to the redirected URL, so we in effect restore the Authorization + // header. + if ((status == 403) && self.usingBackgroundSession) { + NSURL *redirectURL = self.response.URL; + NSURLRequest *request = self.request; + if (![request.URL isEqual:redirectURL]) { + NSString *authorizationHeader = [request.allHTTPHeaderFields objectForKey:@"Authorization"]; + if (authorizationHeader != nil) { + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.URL = redirectURL; + [self updateMutableRequest:mutableRequest]; + // Avoid assuming the session is still valid. + self.session = nil; + forceAssumeRetry = YES; + } + } + } + + // If invalidating the session was deferred in stopFetchReleasingCallbacks: then do it now. + NSURLSession *oldSession = self.sessionNeedingInvalidation; + if (oldSession) { + [self setSessionNeedingInvalidation:NULL]; + [oldSession finishTasksAndInvalidate]; + } + + // Failed. + [self shouldRetryNowForStatus:status + error:error + forceAssumeRetry:forceAssumeRetry + response:^(BOOL shouldRetry) { + [self finishWithError:error shouldRetry:shouldRetry]; + }]; +} + +#if TARGET_OS_IPHONE +- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSessionDidFinishEventsForBackgroundURLSession:%@", + [self class], self, session); + [self removePersistedBackgroundSessionFromDefaults]; + + GTMSessionFetcherSystemCompletionHandler handler; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + handler = self.systemCompletionHandler; + self.systemCompletionHandler = nil; + } // @synchronized(self) + if (handler) { + GTM_LOG_BACKGROUND_SESSION(@"%@ %p Calling system completionHandler", [self class], self); + handler(); + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSURLSession *oldSession = _session; + _session = nil; + if (_shouldInvalidateSession) { + [oldSession finishTasksAndInvalidate]; + } + } // @synchronized(self) + } +} +#endif + +- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(GTM_NULLABLE NSError *)error { + // This may happen repeatedly for retries. On authentication callbacks, the retry + // may begin before the prior session sends the didBecomeInvalid delegate message. + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ didBecomeInvalidWithError:%@", + [self class], self, session, error); + if (session == (NSURLSession *)self.session) { + GTM_LOG_SESSION_DELEGATE(@" Unexpected retained invalid session: %@", session); + self.session = nil; + } +} + +- (void)finishWithError:(GTM_NULLABLE NSError *)error shouldRetry:(BOOL)shouldRetry { + [self removePersistedBackgroundSessionFromDefaults]; + + BOOL shouldStopFetching = YES; + NSData *downloadedData = nil; +#if !STRIP_GTM_FETCH_LOGGING + BOOL shouldDeferLogging = NO; +#endif + BOOL shouldBeginRetryTimer = NO; + NSInteger status = [self statusCode]; + NSURL *destinationURL = self.destinationFileURL; + + BOOL fetchSucceeded = (error == nil && status >= 0 && status < 300); + +#if !STRIP_GTM_FETCH_LOGGING + if (!fetchSucceeded) { + if (!shouldDeferLogging && !self.hasLoggedError) { + [self logNowWithError:error]; + self.hasLoggedError = YES; + } + } +#endif // !STRIP_GTM_FETCH_LOGGING + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + +#if !STRIP_GTM_FETCH_LOGGING + shouldDeferLogging = _deferResponseBodyLogging; +#endif + if (fetchSucceeded) { + // Success + if ((_downloadedData.length > 0) && (destinationURL != nil)) { + // Overwrite any previous file at the destination URL. + NSFileManager *fileMgr = [NSFileManager defaultManager]; + [fileMgr removeItemAtURL:destinationURL + error:NULL]; + NSURL *destinationFolderURL = [destinationURL URLByDeletingLastPathComponent]; + BOOL didMoveDownload = NO; + if ([fileMgr createDirectoryAtURL:destinationFolderURL + withIntermediateDirectories:YES + attributes:nil + error:&error]) { + didMoveDownload = [_downloadedData writeToURL:destinationURL + options:NSDataWritingAtomic + error:&error]; + } + if (didMoveDownload) { + _downloadedData = nil; + } else { + _downloadFinishedError = error; + } + } + downloadedData = _downloadedData; + } else { + // Unsuccessful with error or status over 300. Retry or notify the delegate of failure + if (shouldRetry) { + // Retrying. + shouldBeginRetryTimer = YES; + shouldStopFetching = NO; + } else { + if (error == nil) { + // Create an error. + NSDictionary *userInfo = GTMErrorUserInfoForData( + _downloadedData.length > 0 ? _downloadedData : _downloadTaskErrorData, + [self responseHeadersUnsynchronized]); + + error = [NSError errorWithDomain:kGTMSessionFetcherStatusDomain + code:status + userInfo:userInfo]; + } else { + // If the error had resume data, and the client supplied a resume block, pass the + // data to the client. + void (^resumeBlock)(NSData *) = _resumeDataBlock; + _resumeDataBlock = nil; + if (resumeBlock) { + NSData *resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData]; + if (resumeData) { + [self invokeOnCallbackQueueAfterUserStopped:YES block:^{ + resumeBlock(resumeData); + }]; + } + } + } + if (_downloadedData.length > 0) { + downloadedData = _downloadedData; + } + // If the error occurred after retries, report the number and duration of the + // retries. This provides a clue to a developer looking at the error description + // that the fetcher did retry before failing with this error. + if (_retryCount > 0) { + NSMutableDictionary *userInfoWithRetries = + [NSMutableDictionary dictionaryWithDictionary:(NSDictionary *)error.userInfo]; + NSTimeInterval timeSinceInitialRequest = -[_initialRequestDate timeIntervalSinceNow]; + [userInfoWithRetries setObject:@(timeSinceInitialRequest) + forKey:kGTMSessionFetcherElapsedIntervalWithRetriesKey]; + [userInfoWithRetries setObject:@(_retryCount) + forKey:kGTMSessionFetcherNumberOfRetriesDoneKey]; + error = [NSError errorWithDomain:(NSString *)error.domain + code:error.code + userInfo:userInfoWithRetries]; + } + } + } + } // @synchronized(self) + + if (shouldBeginRetryTimer) { + [self beginRetryTimer]; + } + + // We want to send the stop notification before calling the delegate's + // callback selector, since the callback selector may release all of + // the fetcher properties that the client is using to track the fetches. + // + // We'll also stop now so that, to any observers watching the notifications, + // it doesn't look like our wait for a retry (which may be long, + // 30 seconds or more) is part of the network activity. + [self sendStopNotificationIfNeeded]; + + if (shouldStopFetching) { + [self invokeFetchCallbacksOnCallbackQueueWithData:downloadedData + error:error]; + // The upload subclass doesn't want to release callbacks until upload chunks have completed. + BOOL shouldRelease = [self shouldReleaseCallbacksUponCompletion]; + [self stopFetchReleasingCallbacks:shouldRelease]; + } + +#if !STRIP_GTM_FETCH_LOGGING + // _hasLoggedError is only set by this method + if (!shouldDeferLogging && !_hasLoggedError) { + [self logNowWithError:error]; + } +#endif +} + +- (BOOL)shouldReleaseCallbacksUponCompletion { + // A subclass can override this to keep callbacks around after the + // connection has finished successfully + return YES; +} + +- (void)logNowWithError:(GTM_NULLABLE NSError *)error { + GTMSessionCheckNotSynchronized(self); + + // If the logging category is available, then log the current request, + // response, data, and error + if ([self respondsToSelector:@selector(logFetchWithError:)]) { + [self performSelector:@selector(logFetchWithError:) withObject:error]; + } +} + +#pragma mark Retries + +- (BOOL)isRetryError:(NSError *)error { + struct RetryRecord { + __unsafe_unretained NSString *const domain; + NSInteger code; + }; + + struct RetryRecord retries[] = { + { kGTMSessionFetcherStatusDomain, 408 }, // request timeout + { kGTMSessionFetcherStatusDomain, 502 }, // failure gatewaying to another server + { kGTMSessionFetcherStatusDomain, 503 }, // service unavailable + { kGTMSessionFetcherStatusDomain, 504 }, // request timeout + { NSURLErrorDomain, NSURLErrorTimedOut }, + { NSURLErrorDomain, NSURLErrorNetworkConnectionLost }, + { nil, 0 } + }; + + // NSError's isEqual always returns false for equal but distinct instances + // of NSError, so we have to compare the domain and code values explicitly + NSString *domain = error.domain; + NSInteger code = error.code; + for (int idx = 0; retries[idx].domain != nil; idx++) { + if (code == retries[idx].code && [domain isEqual:retries[idx].domain]) { + return YES; + } + } + return NO; +} + +// shouldRetryNowForStatus:error: responds with YES if the user has enabled retries +// and the status or error is one that is suitable for retrying. "Suitable" +// means either the isRetryError:'s list contains the status or error, or the +// user's retry block is present and returns YES when called, or the +// authorizer may be able to fix. +- (void)shouldRetryNowForStatus:(NSInteger)status + error:(NSError *)error + forceAssumeRetry:(BOOL)forceAssumeRetry + response:(GTMSessionFetcherRetryResponse)response { + // Determine if a refreshed authorizer may avoid an authorization error + BOOL willRetry = NO; + + // We assume _authorizer is immutable after beginFetch, and _hasAttemptedAuthRefresh is modified + // only in this method, and this method is invoked on the serial delegate queue. + // + // We want to avoid calling the authorizer from inside a sync block. + BOOL isFirstAuthError = (_authorizer != nil + && !_hasAttemptedAuthRefresh + && status == GTMSessionFetcherStatusUnauthorized); // 401 + + BOOL hasPrimed = NO; + if (isFirstAuthError) { + if ([_authorizer respondsToSelector:@selector(primeForRefresh)]) { + hasPrimed = [_authorizer primeForRefresh]; + } + } + + BOOL shouldRetryForAuthRefresh = NO; + if (hasPrimed) { + shouldRetryForAuthRefresh = YES; + _hasAttemptedAuthRefresh = YES; + [self updateRequestValue:nil forHTTPHeaderField:@"Authorization"]; + } + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + BOOL shouldDoRetry = [self isRetryEnabledUnsynchronized]; + if (shouldDoRetry && ![self hasRetryAfterInterval]) { + + // Determine if we're doing exponential backoff retries + shouldDoRetry = [self nextRetryIntervalUnsynchronized] < _maxRetryInterval; + + if (shouldDoRetry) { + // If an explicit max retry interval was set, we expect repeated backoffs to take + // up to roughly twice that for repeated fast failures. If the initial attempt is + // already more than 3 times the max retry interval, then failures have taken a long time + // (such as from network timeouts) so don't retry again to avoid the app becoming + // unexpectedly unresponsive. + if (_maxRetryInterval > 0) { + NSTimeInterval maxAllowedIntervalBeforeRetry = _maxRetryInterval * 3; + NSTimeInterval timeSinceInitialRequest = -[_initialRequestDate timeIntervalSinceNow]; + if (timeSinceInitialRequest > maxAllowedIntervalBeforeRetry) { + shouldDoRetry = NO; + } + } + } + } + BOOL canRetry = shouldRetryForAuthRefresh || forceAssumeRetry || shouldDoRetry; + if (canRetry) { + NSDictionary *userInfo = + GTMErrorUserInfoForData(_downloadedData, [self responseHeadersUnsynchronized]); + NSError *statusError = [NSError errorWithDomain:kGTMSessionFetcherStatusDomain + code:status + userInfo:userInfo]; + if (error == nil) { + error = statusError; + } + willRetry = shouldRetryForAuthRefresh || + forceAssumeRetry || + [self isRetryError:error] || + ((error != statusError) && [self isRetryError:statusError]); + + // If the user has installed a retry callback, consult that. + GTMSessionFetcherRetryBlock retryBlock = _retryBlock; + if (retryBlock) { + [self invokeOnCallbackQueueUnlessStopped:^{ + retryBlock(willRetry, error, response); + }]; + return; + } + } + } // @synchronized(self) + response(willRetry); +} + +- (BOOL)hasRetryAfterInterval { + GTMSessionCheckSynchronized(self); + + NSDictionary *responseHeaders = [self responseHeadersUnsynchronized]; + NSString *retryAfterValue = [responseHeaders valueForKey:@"Retry-After"]; + return (retryAfterValue != nil); +} + +- (NSTimeInterval)retryAfterInterval { + GTMSessionCheckSynchronized(self); + + NSDictionary *responseHeaders = [self responseHeadersUnsynchronized]; + NSString *retryAfterValue = [responseHeaders valueForKey:@"Retry-After"]; + if (retryAfterValue == nil) { + return 0; + } + // Retry-After formatted as HTTP-date | delta-seconds + // Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + NSDateFormatter *rfc1123DateFormatter = [[NSDateFormatter alloc] init]; + rfc1123DateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + rfc1123DateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + rfc1123DateFormatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss z"; + NSDate *retryAfterDate = [rfc1123DateFormatter dateFromString:retryAfterValue]; + NSTimeInterval retryAfterInterval = (retryAfterDate != nil) ? + retryAfterDate.timeIntervalSinceNow : retryAfterValue.intValue; + retryAfterInterval = MAX(0, retryAfterInterval); + return retryAfterInterval; +} + +- (void)beginRetryTimer { + if (![NSThread isMainThread]) { + // Defer creating and starting the timer until we're on the main thread to ensure it has + // a run loop. + dispatch_group_async(_callbackGroup, dispatch_get_main_queue(), ^{ + [self beginRetryTimer]; + }); + return; + } + + [self destroyRetryTimer]; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSTimeInterval nextInterval = [self nextRetryIntervalUnsynchronized]; + NSTimeInterval maxInterval = _maxRetryInterval; + NSTimeInterval newInterval = MIN(nextInterval, (maxInterval > 0 ? maxInterval : DBL_MAX)); + NSTimeInterval newIntervalTolerance = (newInterval / 10) > 1.0 ?: 1.0; + + _lastRetryInterval = newInterval; + + _retryTimer = [NSTimer timerWithTimeInterval:newInterval + target:self + selector:@selector(retryTimerFired:) + userInfo:nil + repeats:NO]; + _retryTimer.tolerance = newIntervalTolerance; + [[NSRunLoop mainRunLoop] addTimer:_retryTimer + forMode:NSDefaultRunLoopMode]; + } // @synchronized(self) + + [self postNotificationOnMainThreadWithName:kGTMSessionFetcherRetryDelayStartedNotification + userInfo:nil + requireAsync:NO]; +} + +- (void)retryTimerFired:(NSTimer *)timer { + [self destroyRetryTimer]; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _retryCount++; + } // @synchronized(self) + + NSOperationQueue *queue = self.sessionDelegateQueue; + [queue addOperationWithBlock:^{ + [self retryFetch]; + }]; +} + +- (void)destroyRetryTimer { + BOOL shouldNotify = NO; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_retryTimer) { + [_retryTimer invalidate]; + _retryTimer = nil; + shouldNotify = YES; + } + } + + if (shouldNotify) { + [self postNotificationOnMainThreadWithName:kGTMSessionFetcherRetryDelayStoppedNotification + userInfo:nil + requireAsync:NO]; + } +} + +- (NSUInteger)retryCount { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _retryCount; + } // @synchronized(self) +} + +- (NSTimeInterval)nextRetryInterval { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSTimeInterval interval = [self nextRetryIntervalUnsynchronized]; + return interval; + } // @synchronized(self) +} + +- (NSTimeInterval)nextRetryIntervalUnsynchronized { + GTMSessionCheckSynchronized(self); + + NSInteger statusCode = [self statusCodeUnsynchronized]; + if ((statusCode == 503) && [self hasRetryAfterInterval]) { + NSTimeInterval secs = [self retryAfterInterval]; + return secs; + } + // The next wait interval is the factor (2.0) times the last interval, + // but never less than the minimum interval. + NSTimeInterval secs = _lastRetryInterval * _retryFactor; + if (_maxRetryInterval > 0) { + secs = MIN(secs, _maxRetryInterval); + } + secs = MAX(secs, _minRetryInterval); + + return secs; +} + +- (NSTimer *)retryTimer { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _retryTimer; + } // @synchronized(self) +} + +- (BOOL)isRetryEnabled { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _isRetryEnabled; + } // @synchronized(self) +} + +- (BOOL)isRetryEnabledUnsynchronized { + GTMSessionCheckSynchronized(self); + + return _isRetryEnabled; +} + +- (void)setRetryEnabled:(BOOL)flag { + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (flag && !_isRetryEnabled) { + // We defer initializing these until the user calls setRetryEnabled + // to avoid using the random number generator if it's not needed. + // However, this means min and max intervals for this fetcher are reset + // as a side effect of calling setRetryEnabled. + // + // Make an initial retry interval random between 1.0 and 2.0 seconds + _minRetryInterval = InitialMinRetryInterval(); + _maxRetryInterval = kUnsetMaxRetryInterval; + _retryFactor = 2.0; + _lastRetryInterval = 0.0; + } + _isRetryEnabled = flag; + } // @synchronized(self) +}; + +- (NSTimeInterval)maxRetryInterval { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _maxRetryInterval; + } // @synchronized(self) +} + +- (void)setMaxRetryInterval:(NSTimeInterval)secs { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (secs > 0) { + _maxRetryInterval = secs; + } else { + _maxRetryInterval = kUnsetMaxRetryInterval; + } + } // @synchronized(self) +} + +- (double)minRetryInterval { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _minRetryInterval; + } // @synchronized(self) +} + +- (void)setMinRetryInterval:(NSTimeInterval)secs { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (secs > 0) { + _minRetryInterval = secs; + } else { + // Set min interval to a random value between 1.0 and 2.0 seconds + // so that if multiple clients start retrying at the same time, they'll + // repeat at different times and avoid overloading the server + _minRetryInterval = InitialMinRetryInterval(); + } + } // @synchronized(self) + +} + +#pragma mark iOS System Completion Handlers + +#if TARGET_OS_IPHONE +static NSMutableDictionary *gSystemCompletionHandlers = nil; + +- (GTM_NULLABLE GTMSessionFetcherSystemCompletionHandler)systemCompletionHandler { + return [[self class] systemCompletionHandlerForSessionIdentifier:_sessionIdentifier]; +} + +- (void)setSystemCompletionHandler:(GTM_NULLABLE GTMSessionFetcherSystemCompletionHandler)systemCompletionHandler { + [[self class] setSystemCompletionHandler:systemCompletionHandler + forSessionIdentifier:_sessionIdentifier]; +} + ++ (void)setSystemCompletionHandler:(GTM_NULLABLE GTMSessionFetcherSystemCompletionHandler)systemCompletionHandler + forSessionIdentifier:(NSString *)sessionIdentifier { + if (!sessionIdentifier) { + NSLog(@"%s with nil identifier", __PRETTY_FUNCTION__); + return; + } + + @synchronized([GTMSessionFetcher class]) { + if (gSystemCompletionHandlers == nil && systemCompletionHandler != nil) { + gSystemCompletionHandlers = [[NSMutableDictionary alloc] init]; + } + // Use setValue: to remove the object if completionHandler is nil. + [gSystemCompletionHandlers setValue:systemCompletionHandler + forKey:sessionIdentifier]; + } +} + ++ (GTM_NULLABLE GTMSessionFetcherSystemCompletionHandler)systemCompletionHandlerForSessionIdentifier:(NSString *)sessionIdentifier { + if (!sessionIdentifier) { + return nil; + } + @synchronized([GTMSessionFetcher class]) { + return [gSystemCompletionHandlers objectForKey:sessionIdentifier]; + } +} +#endif // TARGET_OS_IPHONE + +#pragma mark Getters and Setters + +@synthesize downloadResumeData = _downloadResumeData, + configuration = _configuration, + configurationBlock = _configurationBlock, + sessionTask = _sessionTask, + wasCreatedFromBackgroundSession = _wasCreatedFromBackgroundSession, + sessionUserInfo = _sessionUserInfo, + taskDescription = _taskDescription, + taskPriority = _taskPriority, + usingBackgroundSession = _usingBackgroundSession, + canShareSession = _canShareSession, + completionHandler = _completionHandler, + credential = _credential, + proxyCredential = _proxyCredential, + bodyData = _bodyData, + bodyLength = _bodyLength, + service = _service, + serviceHost = _serviceHost, + accumulateDataBlock = _accumulateDataBlock, + receivedProgressBlock = _receivedProgressBlock, + downloadProgressBlock = _downloadProgressBlock, + resumeDataBlock = _resumeDataBlock, + didReceiveResponseBlock = _didReceiveResponseBlock, + challengeBlock = _challengeBlock, + willRedirectBlock = _willRedirectBlock, + sendProgressBlock = _sendProgressBlock, + willCacheURLResponseBlock = _willCacheURLResponseBlock, + retryBlock = _retryBlock, + retryFactor = _retryFactor, + allowedInsecureSchemes = _allowedInsecureSchemes, + allowLocalhostRequest = _allowLocalhostRequest, + allowInvalidServerCertificates = _allowInvalidServerCertificates, + cookieStorage = _cookieStorage, + callbackQueue = _callbackQueue, + initialBeginFetchDate = _initialBeginFetchDate, + testBlock = _testBlock, + testBlockAccumulateDataChunkCount = _testBlockAccumulateDataChunkCount, + comment = _comment, + log = _log; + +#if !STRIP_GTM_FETCH_LOGGING +@synthesize redirectedFromURL = _redirectedFromURL, + logRequestBody = _logRequestBody, + logResponseBody = _logResponseBody, + hasLoggedError = _hasLoggedError; +#endif + +#if GTM_BACKGROUND_TASK_FETCHING +@synthesize backgroundTaskIdentifier = _backgroundTaskIdentifier, + skipBackgroundTask = _skipBackgroundTask; +#endif + +- (GTM_NULLABLE NSURLRequest *)request { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_request copy]; + } // @synchronized(self) +} + +- (void)setRequest:(GTM_NULLABLE NSURLRequest *)request { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (![self isFetchingUnsynchronized]) { + _request = [request mutableCopy]; + } else { + GTMSESSION_ASSERT_DEBUG(0, @"request may not be set after beginFetch has been invoked"); + } + } // @synchronized(self) +} + +- (GTM_NULLABLE NSMutableURLRequest *)mutableRequestForTesting { + // Allow tests only to modify the request, useful during retries. + return _request; +} + +// Internal method for updating the request property such as on redirects. +- (void)updateMutableRequest:(GTM_NULLABLE NSMutableURLRequest *)request { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _request = request; + } // @synchronized(self) +} + +// Set a header field value on the request. Header field value changes will not +// affect a fetch after the fetch has begun. +- (void)setRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field { + if (![self isFetching]) { + [self updateRequestValue:value forHTTPHeaderField:field]; + } else { + GTMSESSION_ASSERT_DEBUG(0, @"request may not be set after beginFetch has been invoked"); + } +} + +// Internal method for updating request headers. +- (void)updateRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [_request setValue:value forHTTPHeaderField:field]; + } // @synchronized(self) +} + +- (void)setResponse:(GTM_NULLABLE NSURLResponse *)response { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _response = response; + } // @synchronized(self) +} + +- (int64_t)bodyLength { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_bodyLength == NSURLSessionTransferSizeUnknown) { + if (_bodyData) { + _bodyLength = (int64_t)_bodyData.length; + } else if (_bodyFileURL) { + NSNumber *fileSizeNum = nil; + NSError *fileSizeError = nil; + if ([_bodyFileURL getResourceValue:&fileSizeNum + forKey:NSURLFileSizeKey + error:&fileSizeError]) { + _bodyLength = [fileSizeNum longLongValue]; + } + } + } + return _bodyLength; + } // @synchronized(self) +} + +- (BOOL)useUploadTask { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _useUploadTask; + } // @synchronized(self) +} + +- (void)setUseUploadTask:(BOOL)flag { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (flag != _useUploadTask) { + GTMSESSION_ASSERT_DEBUG(![self isFetchingUnsynchronized], + @"useUploadTask should not change after beginFetch has been invoked"); + _useUploadTask = flag; + } + } // @synchronized(self) +} + +- (GTM_NULLABLE NSURL *)bodyFileURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _bodyFileURL; + } // @synchronized(self) +} + +- (void)setBodyFileURL:(GTM_NULLABLE NSURL *)fileURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // The comparison here is a trivial optimization and forgiveness for any client that + // repeatedly sets the property, so it just uses pointer comparison rather than isEqual:. + if (fileURL != _bodyFileURL) { + GTMSESSION_ASSERT_DEBUG(![self isFetchingUnsynchronized], + @"fileURL should not change after beginFetch has been invoked"); + + _bodyFileURL = fileURL; + } + } // @synchronized(self) +} + +- (GTM_NULLABLE GTMSessionFetcherBodyStreamProvider)bodyStreamProvider { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _bodyStreamProvider; + } // @synchronized(self) +} + +- (void)setBodyStreamProvider:(GTM_NULLABLE GTMSessionFetcherBodyStreamProvider)block { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSESSION_ASSERT_DEBUG(![self isFetchingUnsynchronized], + @"stream provider should not change after beginFetch has been invoked"); + + _bodyStreamProvider = [block copy]; + } // @synchronized(self) +} + +- (GTM_NULLABLE id<GTMFetcherAuthorizationProtocol>)authorizer { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _authorizer; + } // @synchronized(self) +} + +- (void)setAuthorizer:(GTM_NULLABLE id<GTMFetcherAuthorizationProtocol>)authorizer { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (authorizer != _authorizer) { + if ([self isFetchingUnsynchronized]) { + GTMSESSION_ASSERT_DEBUG(0, @"authorizer should not change after beginFetch has been invoked"); + } else { + _authorizer = authorizer; + } + } + } // @synchronized(self) +} + +- (GTM_NULLABLE NSData *)downloadedData { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _downloadedData; + } // @synchronized(self) +} + +- (void)setDownloadedData:(GTM_NULLABLE NSData *)data { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _downloadedData = [data mutableCopy]; + } // @synchronized(self) +} + +- (int64_t)downloadedLength { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _downloadedLength; + } // @synchronized(self) +} + +- (void)setDownloadedLength:(int64_t)length { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _downloadedLength = length; + } // @synchronized(self) +} + +- (dispatch_queue_t GTM_NONNULL_TYPE)callbackQueue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _callbackQueue; + } // @synchronized(self) +} + +- (void)setCallbackQueue:(dispatch_queue_t GTM_NULLABLE_TYPE)queue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _callbackQueue = queue ?: dispatch_get_main_queue(); + } // @synchronized(self) +} + +- (GTM_NULLABLE NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _session; + } // @synchronized(self) +} + +- (NSInteger)servicePriority { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _servicePriority; + } // @synchronized(self) +} + +- (void)setServicePriority:(NSInteger)value { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (value != _servicePriority) { + GTMSESSION_ASSERT_DEBUG(![self isFetchingUnsynchronized], + @"servicePriority should not change after beginFetch has been invoked"); + + _servicePriority = value; + } + } // @synchronized(self) +} + + +- (void)setSession:(GTM_NULLABLE NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _session = session; + } // @synchronized(self) +} + +- (BOOL)canShareSession { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _canShareSession; + } // @synchronized(self) +} + +- (void)setCanShareSession:(BOOL)flag { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _canShareSession = flag; + } // @synchronized(self) +} + +- (BOOL)useBackgroundSession { + // This reflects if the user requested a background session, not necessarily + // if one was created. That is tracked with _usingBackgroundSession. + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _userRequestedBackgroundSession; + } // @synchronized(self) +} + +- (void)setUseBackgroundSession:(BOOL)flag { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (flag != _userRequestedBackgroundSession) { + GTMSESSION_ASSERT_DEBUG(![self isFetchingUnsynchronized], + @"useBackgroundSession should not change after beginFetch has been invoked"); + + _userRequestedBackgroundSession = flag; + } + } // @synchronized(self) +} + +- (BOOL)isUsingBackgroundSession { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _usingBackgroundSession; + } // @synchronized(self) +} + +- (void)setUsingBackgroundSession:(BOOL)flag { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _usingBackgroundSession = flag; + } // @synchronized(self) +} + +- (GTM_NULLABLE NSURLSession *)sessionNeedingInvalidation { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _sessionNeedingInvalidation; + } // @synchronized(self) +} + +- (void)setSessionNeedingInvalidation:(GTM_NULLABLE NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _sessionNeedingInvalidation = session; + } // @synchronized(self) +} + +- (NSOperationQueue * GTM_NONNULL_TYPE)sessionDelegateQueue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _delegateQueue; + } // @synchronized(self) +} + +- (void)setSessionDelegateQueue:(NSOperationQueue * GTM_NULLABLE_TYPE)queue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (queue != _delegateQueue) { + if ([self isFetchingUnsynchronized]) { + GTMSESSION_ASSERT_DEBUG(0, @"sessionDelegateQueue should not change after fetch begins"); + } else { + _delegateQueue = queue ?: [NSOperationQueue mainQueue]; + } + } + } // @synchronized(self) +} + +- (BOOL)userStoppedFetching { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _userStoppedFetching; + } // @synchronized(self) +} + +- (GTM_NULLABLE id)userData { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _userData; + } // @synchronized(self) +} + +- (void)setUserData:(GTM_NULLABLE id)theObj { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _userData = theObj; + } // @synchronized(self) +} + +- (GTM_NULLABLE NSURL *)destinationFileURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _destinationFileURL; + } // @synchronized(self) +} + +- (void)setDestinationFileURL:(GTM_NULLABLE NSURL *)destinationFileURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (((_destinationFileURL == nil) && (destinationFileURL == nil)) || + [_destinationFileURL isEqual:destinationFileURL]) { + return; + } + if (_sessionIdentifier) { + // This is something we don't expect to happen in production. + // However if it ever happen, leave a system log. + NSLog(@"%@: Destination File URL changed from (%@) to (%@) after session identifier has " + @"been created.", + [self class], _destinationFileURL, destinationFileURL); +#if DEBUG + // On both the simulator and devices, the path can change to the download file, but the name + // shouldn't change. Technically, this isn't supported in the fetcher, but the change of + // URL is expected to happen only across development runs through Xcode. + NSString *oldFilename = [_destinationFileURL lastPathComponent]; + NSString *newFilename = [destinationFileURL lastPathComponent]; + #pragma unused(oldFilename) + #pragma unused(newFilename) + GTMSESSION_ASSERT_DEBUG([oldFilename isEqualToString:newFilename], + @"Destination File URL cannot be changed after session identifier has been created"); +#endif + } + _destinationFileURL = destinationFileURL; + } // @synchronized(self) +} + +- (void)setProperties:(GTM_NULLABLE NSDictionary *)dict { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _properties = [dict mutableCopy]; + } // @synchronized(self) +} + +- (GTM_NULLABLE NSDictionary *)properties { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _properties; + } // @synchronized(self) +} + +- (void)setProperty:(GTM_NULLABLE id)obj forKey:(NSString *)key { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_properties == nil && obj != nil) { + _properties = [[NSMutableDictionary alloc] init]; + } + [_properties setValue:obj forKey:key]; + } // @synchronized(self) +} + +- (GTM_NULLABLE id)propertyForKey:(NSString *)key { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_properties objectForKey:key]; + } // @synchronized(self) +} + +- (void)addPropertiesFromDictionary:(NSDictionary *)dict { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_properties == nil && dict != nil) { + [self setProperties:[dict mutableCopy]]; + } else { + [_properties addEntriesFromDictionary:dict]; + } + } // @synchronized(self) +} + +- (void)setCommentWithFormat:(id)format, ... { +#if !STRIP_GTM_FETCH_LOGGING + NSString *result = format; + if (format) { + va_list argList; + va_start(argList, format); + + result = [[NSString alloc] initWithFormat:format + arguments:argList]; + va_end(argList); + } + [self setComment:result]; +#endif +} + +#if !STRIP_GTM_FETCH_LOGGING +- (NSData *)loggedStreamData { + return _loggedStreamData; +} + +- (void)appendLoggedStreamData:dataToAdd { + if (!_loggedStreamData) { + _loggedStreamData = [NSMutableData data]; + } + [_loggedStreamData appendData:dataToAdd]; +} + +- (void)clearLoggedStreamData { + _loggedStreamData = nil; +} + +- (void)setDeferResponseBodyLogging:(BOOL)deferResponseBodyLogging { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (deferResponseBodyLogging != _deferResponseBodyLogging) { + _deferResponseBodyLogging = deferResponseBodyLogging; + if (!deferResponseBodyLogging && !self.hasLoggedError) { + [_delegateQueue addOperationWithBlock:^{ + [self logNowWithError:nil]; + }]; + } + } + } // @synchronized(self) +} + +- (BOOL)deferResponseBodyLogging { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _deferResponseBodyLogging; + } // @synchronized(self) +} + +#else ++ (void)setLoggingEnabled:(BOOL)flag { +} + ++ (BOOL)isLoggingEnabled { + return NO; +} +#endif // STRIP_GTM_FETCH_LOGGING + +@end + +@implementation GTMSessionFetcher (BackwardsCompatibilityOnly) + +- (void)setCookieStorageMethod:(NSInteger)method { + // For backwards compatibility with the old fetcher, we'll support the old constants. + // + // Clients using the GTMSessionFetcher class should set the cookie storage explicitly + // themselves. + NSHTTPCookieStorage *storage = nil; + switch(method) { + case 0: // kGTMHTTPFetcherCookieStorageMethodStatic + // nil storage will use [[self class] staticCookieStorage] when the fetch begins. + break; + case 1: // kGTMHTTPFetcherCookieStorageMethodFetchHistory + // Do nothing; use whatever was set by the fetcher service. + return; + case 2: // kGTMHTTPFetcherCookieStorageMethodSystemDefault + storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + break; + case 3: // kGTMHTTPFetcherCookieStorageMethodNone + // Create temporary storage for this fetcher only. + storage = [[GTMSessionCookieStorage alloc] init]; + break; + default: + GTMSESSION_ASSERT_DEBUG(0, @"Invalid cookie storage method: %d", (int)method); + } + self.cookieStorage = storage; +} + +@end + +@implementation GTMSessionCookieStorage { + NSMutableArray *_cookies; + NSHTTPCookieAcceptPolicy _policy; +} + +- (id)init { + self = [super init]; + if (self != nil) { + _cookies = [[NSMutableArray alloc] init]; + } + return self; +} + +- (GTM_NULLABLE NSArray *)cookies { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_cookies copy]; + } // @synchronized(self) +} + +- (void)setCookie:(NSHTTPCookie *)cookie { + if (!cookie) return; + if (_policy == NSHTTPCookieAcceptPolicyNever) return; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self internalSetCookie:cookie]; + } // @synchronized(self) +} + +// Note: this should only be called from inside a @synchronized(self) block. +- (void)internalSetCookie:(NSHTTPCookie *)newCookie { + GTMSessionCheckSynchronized(self); + + if (_policy == NSHTTPCookieAcceptPolicyNever) return; + + BOOL isValidCookie = (newCookie.name.length > 0 + && newCookie.domain.length > 0 + && newCookie.path.length > 0); + GTMSESSION_ASSERT_DEBUG(isValidCookie, @"invalid cookie: %@", newCookie); + + if (isValidCookie) { + // Remove the cookie if it's currently in the array. + NSHTTPCookie *oldCookie = [self cookieMatchingCookie:newCookie]; + if (oldCookie) { + [_cookies removeObjectIdenticalTo:oldCookie]; + } + + if (![[self class] hasCookieExpired:newCookie]) { + [_cookies addObject:newCookie]; + } + } +} + +// Add all cookies in the new cookie array to the storage, +// replacing stored cookies as appropriate. +// +// Side effect: removes expired cookies from the storage array. +- (void)setCookies:(GTM_NULLABLE NSArray *)newCookies { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self removeExpiredCookies]; + + for (NSHTTPCookie *newCookie in newCookies) { + [self internalSetCookie:newCookie]; + } + } // @synchronized(self) +} + +- (void)setCookies:(NSArray *)cookies forURL:(GTM_NULLABLE NSURL *)URL mainDocumentURL:(GTM_NULLABLE NSURL *)mainDocumentURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_policy == NSHTTPCookieAcceptPolicyNever) { + return; + } + + if (_policy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain) { + NSString *mainHost = mainDocumentURL.host; + NSString *associatedHost = URL.host; + if (!mainHost || ![associatedHost hasSuffix:mainHost]) { + return; + } + } + } // @synchronized(self) + [self setCookies:cookies]; +} + +- (void)deleteCookie:(NSHTTPCookie *)cookie { + if (!cookie) return; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSHTTPCookie *foundCookie = [self cookieMatchingCookie:cookie]; + if (foundCookie) { + [_cookies removeObjectIdenticalTo:foundCookie]; + } + } // @synchronized(self) +} + +// Retrieve all cookies appropriate for the given URL, considering +// domain, path, cookie name, expiration, security setting. +// Side effect: removed expired cookies from the storage array. +- (GTM_NULLABLE NSArray *)cookiesForURL:(NSURL *)theURL { + NSMutableArray *foundCookies = nil; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self removeExpiredCookies]; + + // We'll prepend "." to the desired domain, since we want the + // actual domain "nytimes.com" to still match the cookie domain + // ".nytimes.com" when we check it below with hasSuffix. + NSString *host = theURL.host.lowercaseString; + NSString *path = theURL.path; + NSString *scheme = [theURL scheme]; + + NSString *requestingDomain = nil; + BOOL isLocalhostRetrieval = NO; + + if (IsLocalhost(host)) { + isLocalhostRetrieval = YES; + } else { + if (host.length > 0) { + requestingDomain = [@"." stringByAppendingString:host]; + } + } + + for (NSHTTPCookie *storedCookie in _cookies) { + NSString *cookieDomain = storedCookie.domain.lowercaseString; + NSString *cookiePath = storedCookie.path; + BOOL cookieIsSecure = [storedCookie isSecure]; + + BOOL isDomainOK; + + if (isLocalhostRetrieval) { + // Prior to 10.5.6, the domain stored into NSHTTPCookies for localhost + // is "localhost.local" + isDomainOK = (IsLocalhost(cookieDomain) + || [cookieDomain isEqual:@"localhost.local"]); + } else { + // Ensure we're matching exact domain names. We prepended a dot to the + // requesting domain, so we can also prepend one here if needed before + // checking if the request contains the cookie domain. + if (![cookieDomain hasPrefix:@"."]) { + cookieDomain = [@"." stringByAppendingString:cookieDomain]; + } + isDomainOK = [requestingDomain hasSuffix:cookieDomain]; + } + + BOOL isPathOK = [cookiePath isEqual:@"/"] || [path hasPrefix:cookiePath]; + BOOL isSecureOK = (!cookieIsSecure + || [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame); + + if (isDomainOK && isPathOK && isSecureOK) { + if (foundCookies == nil) { + foundCookies = [NSMutableArray array]; + } + [foundCookies addObject:storedCookie]; + } + } + } // @synchronized(self) + return foundCookies; +} + +// Override methods from the NSHTTPCookieStorage (NSURLSessionTaskAdditions) category. +- (void)storeCookies:(NSArray *)cookies forTask:(NSURLSessionTask *)task { + NSURLRequest *currentRequest = task.currentRequest; + [self setCookies:cookies forURL:currentRequest.URL mainDocumentURL:nil]; +} + +- (void)getCookiesForTask:(NSURLSessionTask *)task + completionHandler:(void (^)(GTM_NSArrayOf(NSHTTPCookie *) *))completionHandler { + if (completionHandler) { + NSURLRequest *currentRequest = task.currentRequest; + NSURL *currentRequestURL = currentRequest.URL; + NSArray *cookies = [self cookiesForURL:currentRequestURL]; + completionHandler(cookies); + } +} + +// Return a cookie from the array with the same name, domain, and path as the +// given cookie, or else return nil if none found. +// +// Both the cookie being tested and all cookies in the storage array should +// be valid (non-nil name, domains, paths). +// +// Note: this should only be called from inside a @synchronized(self) block +- (GTM_NULLABLE NSHTTPCookie *)cookieMatchingCookie:(NSHTTPCookie *)cookie { + GTMSessionCheckSynchronized(self); + + NSString *name = cookie.name; + NSString *domain = cookie.domain; + NSString *path = cookie.path; + + GTMSESSION_ASSERT_DEBUG(name && domain && path, + @"Invalid stored cookie (name:%@ domain:%@ path:%@)", name, domain, path); + + for (NSHTTPCookie *storedCookie in _cookies) { + if ([storedCookie.name isEqual:name] + && [storedCookie.domain isEqual:domain] + && [storedCookie.path isEqual:path]) { + return storedCookie; + } + } + return nil; +} + +// Internal routine to remove any expired cookies from the array, excluding +// cookies with nil expirations. +// +// Note: this should only be called from inside a @synchronized(self) block +- (void)removeExpiredCookies { + GTMSessionCheckSynchronized(self); + + // Count backwards since we're deleting items from the array + for (NSInteger idx = (NSInteger)_cookies.count - 1; idx >= 0; idx--) { + NSHTTPCookie *storedCookie = [_cookies objectAtIndex:(NSUInteger)idx]; + if ([[self class] hasCookieExpired:storedCookie]) { + [_cookies removeObjectAtIndex:(NSUInteger)idx]; + } + } +} + ++ (BOOL)hasCookieExpired:(NSHTTPCookie *)cookie { + NSDate *expiresDate = [cookie expiresDate]; + if (expiresDate == nil) { + // Cookies seem to have a Expires property even when the expiresDate method returns nil. + id expiresVal = [[cookie properties] objectForKey:NSHTTPCookieExpires]; + if ([expiresVal isKindOfClass:[NSDate class]]) { + expiresDate = expiresVal; + } + } + BOOL hasExpired = (expiresDate != nil && [expiresDate timeIntervalSinceNow] < 0); + return hasExpired; +} + +- (void)removeAllCookies { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [_cookies removeAllObjects]; + } // @synchronized(self) +} + +- (NSHTTPCookieAcceptPolicy)cookieAcceptPolicy { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _policy; + } // @synchronized(self) +} + +- (void)setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy)cookieAcceptPolicy { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _policy = cookieAcceptPolicy; + } // @synchronized(self) +} + +@end + +void GTMSessionFetcherAssertValidSelector(id GTM_NULLABLE_TYPE obj, SEL GTM_NULLABLE_TYPE sel, ...) { + // Verify that the object's selector is implemented with the proper + // number and type of arguments +#if DEBUG + va_list argList; + va_start(argList, sel); + + if (obj && sel) { + // Check that the selector is implemented + if (![obj respondsToSelector:sel]) { + NSLog(@"\"%@\" selector \"%@\" is unimplemented or misnamed", + NSStringFromClass([(id)obj class]), + NSStringFromSelector((SEL)sel)); + NSCAssert(0, @"callback selector unimplemented or misnamed"); + } else { + const char *expectedArgType; + unsigned int argCount = 2; // skip self and _cmd + NSMethodSignature *sig = [obj methodSignatureForSelector:sel]; + + // Check that each expected argument is present and of the correct type + while ((expectedArgType = va_arg(argList, const char*)) != 0) { + + if ([sig numberOfArguments] > argCount) { + const char *foundArgType = [sig getArgumentTypeAtIndex:argCount]; + + if (0 != strncmp(foundArgType, expectedArgType, strlen(expectedArgType))) { + NSLog(@"\"%@\" selector \"%@\" argument %d should be type %s", + NSStringFromClass([(id)obj class]), + NSStringFromSelector((SEL)sel), (argCount - 2), expectedArgType); + NSCAssert(0, @"callback selector argument type mistake"); + } + } + argCount++; + } + + // Check that the proper number of arguments are present in the selector + if (argCount != [sig numberOfArguments]) { + NSLog(@"\"%@\" selector \"%@\" should have %d arguments", + NSStringFromClass([(id)obj class]), + NSStringFromSelector((SEL)sel), (argCount - 2)); + NSCAssert(0, @"callback selector arguments incorrect"); + } + } + } + + va_end(argList); +#endif +} + +NSString *GTMFetcherCleanedUserAgentString(NSString *str) { + // Reference http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html + // and http://www-archive.mozilla.org/build/user-agent-strings.html + + if (str == nil) return @""; + + NSMutableString *result = [NSMutableString stringWithString:str]; + + // Replace spaces and commas with underscores + [result replaceOccurrencesOfString:@" " + withString:@"_" + options:0 + range:NSMakeRange(0, result.length)]; + [result replaceOccurrencesOfString:@"," + withString:@"_" + options:0 + range:NSMakeRange(0, result.length)]; + + // Delete http token separators and remaining whitespace + static NSCharacterSet *charsToDelete = nil; + if (charsToDelete == nil) { + // Make a set of unwanted characters + NSString *const kSeparators = @"()<>@;:\\\"/[]?={}"; + + NSMutableCharacterSet *mutableChars = + [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; + [mutableChars addCharactersInString:kSeparators]; + charsToDelete = [mutableChars copy]; // hang on to an immutable copy + } + + while (1) { + NSRange separatorRange = [result rangeOfCharacterFromSet:charsToDelete]; + if (separatorRange.location == NSNotFound) break; + + [result deleteCharactersInRange:separatorRange]; + }; + + return result; +} + +NSString *GTMFetcherSystemVersionString(void) { + static NSString *sSavedSystemString; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // The Xcode 8 SDKs finally cleaned up this mess by providing TARGET_OS_OSX + // and TARGET_OS_IOS, but to build with older SDKs, those don't exist and + // instead one has to rely on TARGET_OS_MAC (which is true for iOS, watchOS, + // and tvOS) and TARGET_OS_IPHONE (which is true for iOS, watchOS, tvOS). So + // one has to order these carefully so you pick off the specific things + // first. + // If the code can ever assume Xcode 8 or higher (even when building for + // older OSes), then + // TARGET_OS_MAC -> TARGET_OS_OSX + // TARGET_OS_IPHONE -> TARGET_OS_IOS + // TARGET_IPHONE_SIMULATOR -> TARGET_OS_SIMULATOR +#if TARGET_OS_WATCH + // watchOS - WKInterfaceDevice + + WKInterfaceDevice *currentDevice = [WKInterfaceDevice currentDevice]; + + NSString *rawModel = [currentDevice model]; + NSString *model = GTMFetcherCleanedUserAgentString(rawModel); + + NSString *systemVersion = [currentDevice systemVersion]; + +#if TARGET_OS_SIMULATOR + NSString *hardwareModel = @"sim"; +#else + NSString *hardwareModel; + struct utsname unameRecord; + if (uname(&unameRecord) == 0) { + NSString *machineName = @(unameRecord.machine); + hardwareModel = GTMFetcherCleanedUserAgentString(machineName); + } + if (hardwareModel.length == 0) { + hardwareModel = @"unk"; + } +#endif + + sSavedSystemString = [[NSString alloc] initWithFormat:@"%@/%@ hw/%@", + model, systemVersion, hardwareModel]; + // Example: Apple_Watch/3.0 hw/Watch1_2 +#elif TARGET_OS_TV || TARGET_OS_IPHONE + // iOS and tvOS have UIDevice, use that. + UIDevice *currentDevice = [UIDevice currentDevice]; + + NSString *rawModel = [currentDevice model]; + NSString *model = GTMFetcherCleanedUserAgentString(rawModel); + + NSString *systemVersion = [currentDevice systemVersion]; + +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_SIMULATOR + NSString *hardwareModel = @"sim"; +#else + NSString *hardwareModel; + struct utsname unameRecord; + if (uname(&unameRecord) == 0) { + NSString *machineName = @(unameRecord.machine); + hardwareModel = GTMFetcherCleanedUserAgentString(machineName); + } + if (hardwareModel.length == 0) { + hardwareModel = @"unk"; + } +#endif + + sSavedSystemString = [[NSString alloc] initWithFormat:@"%@/%@ hw/%@", + model, systemVersion, hardwareModel]; + // Example: iPod_Touch/2.2 hw/iPod1_1 + // Example: Apple_TV/9.2 hw/AppleTV5,3 +#elif TARGET_OS_MAC + // Mac build + NSProcessInfo *procInfo = [NSProcessInfo processInfo]; +#if !defined(MAC_OS_X_VERSION_10_10) + BOOL hasOperatingSystemVersion = NO; +#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 + BOOL hasOperatingSystemVersion = + [procInfo respondsToSelector:@selector(operatingSystemVersion)]; +#else + BOOL hasOperatingSystemVersion = YES; +#endif + NSString *versString; + if (hasOperatingSystemVersion) { +#if defined(MAC_OS_X_VERSION_10_10) + // A reference to NSOperatingSystemVersion requires the 10.10 SDK. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +// Disable unguarded availability warning as we can't use the @availability macro until we require +// all clients to build with Xcode 9 or above. + NSOperatingSystemVersion version = procInfo.operatingSystemVersion; +#pragma clang diagnostic pop + versString = [NSString stringWithFormat:@"%ld.%ld.%ld", + (long)version.majorVersion, (long)version.minorVersion, + (long)version.patchVersion]; +#else +#pragma unused(procInfo) +#endif + } else { + // With Gestalt inexplicably deprecated in 10.8, we're reduced to reading + // the system plist file. + NSString *const kPath = @"/System/Library/CoreServices/SystemVersion.plist"; + NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:kPath]; + versString = [plist objectForKey:@"ProductVersion"]; + if (versString.length == 0) { + versString = @"10.?.?"; + } + } + + sSavedSystemString = [[NSString alloc] initWithFormat:@"MacOSX/%@", versString]; +#elif defined(_SYS_UTSNAME_H) + // Foundation-only build + struct utsname unameRecord; + uname(&unameRecord); + + sSavedSystemString = [NSString stringWithFormat:@"%s/%s", + unameRecord.sysname, unameRecord.release]; // "Darwin/8.11.1" +#else +#error No branch taken for a default user agent +#endif + }); + return sSavedSystemString; +} + +NSString *GTMFetcherStandardUserAgentString(NSBundle * GTM_NULLABLE_TYPE bundle) { + NSString *result = [NSString stringWithFormat:@"%@ %@", + GTMFetcherApplicationIdentifier(bundle), + GTMFetcherSystemVersionString()]; + return result; +} + +NSString *GTMFetcherApplicationIdentifier(NSBundle * GTM_NULLABLE_TYPE bundle) { + @synchronized([GTMSessionFetcher class]) { + static NSMutableDictionary *sAppIDMap = nil; + + // If there's a bundle ID, use that; otherwise, use the process name + if (bundle == nil) { + bundle = [NSBundle mainBundle]; + } + NSString *bundleID = [bundle bundleIdentifier]; + if (bundleID == nil) { + bundleID = @""; + } + + NSString *identifier = [sAppIDMap objectForKey:bundleID]; + if (identifier) return identifier; + + // Apps may add a string to the info.plist to uniquely identify different builds. + identifier = [bundle objectForInfoDictionaryKey:@"GTMUserAgentID"]; + if (identifier.length == 0) { + if (bundleID.length > 0) { + identifier = bundleID; + } else { + // Fall back on the procname, prefixed by "proc" to flag that it's + // autogenerated and perhaps unreliable + NSString *procName = [[NSProcessInfo processInfo] processName]; + identifier = [NSString stringWithFormat:@"proc_%@", procName]; + } + } + + // Clean up whitespace and special characters + identifier = GTMFetcherCleanedUserAgentString(identifier); + + // If there's a version number, append that + NSString *version = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if (version.length == 0) { + version = [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + } + + // Clean up whitespace and special characters + version = GTMFetcherCleanedUserAgentString(version); + + // Glue the two together (cleanup done above or else cleanup would strip the + // slash) + if (version.length > 0) { + identifier = [identifier stringByAppendingFormat:@"/%@", version]; + } + + if (sAppIDMap == nil) { + sAppIDMap = [[NSMutableDictionary alloc] init]; + } + [sAppIDMap setObject:identifier forKey:bundleID]; + return identifier; + } +} + +#if DEBUG && (!defined(NS_BLOCK_ASSERTIONS) || GTMSESSION_ASSERT_AS_LOG) +@implementation GTMSessionSyncMonitorInternal { + NSValue *_objectKey; // The synchronize target object. + const char *_functionName; // The function containing the monitored sync block. +} + +- (instancetype)initWithSynchronizationObject:(id)object + allowRecursive:(BOOL)allowRecursive + functionName:(const char *)functionName { + self = [super init]; + if (self) { + Class threadKey = [GTMSessionSyncMonitorInternal class]; + _objectKey = [NSValue valueWithNonretainedObject:object]; + _functionName = functionName; + + NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary; + NSMutableDictionary *counters = threadDict[threadKey]; + if (counters == nil) { + counters = [NSMutableDictionary dictionary]; + threadDict[(id)threadKey] = counters; + } + NSCountedSet *functionNamesCounter = counters[_objectKey]; + NSUInteger numberOfSyncingFunctions = functionNamesCounter.count; + + if (!allowRecursive) { + BOOL isTopLevelSyncScope = (numberOfSyncingFunctions == 0); + NSArray *stack = [NSThread callStackSymbols]; + GTMSESSION_ASSERT_DEBUG(isTopLevelSyncScope, + @"*** Recursive sync on %@ at %s; previous sync at %@\n%@", + [object class], functionName, functionNamesCounter.allObjects, + [stack subarrayWithRange:NSMakeRange(1, stack.count - 1)]); + } + + if (!functionNamesCounter) { + functionNamesCounter = [NSCountedSet set]; + counters[_objectKey] = functionNamesCounter; + } + [functionNamesCounter addObject:(id _Nonnull)@(functionName)]; + } + return self; +} + +- (void)dealloc { + Class threadKey = [GTMSessionSyncMonitorInternal class]; + + NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary; + NSMutableDictionary *counters = threadDict[threadKey]; + NSCountedSet *functionNamesCounter = counters[_objectKey]; + NSString *functionNameStr = @(_functionName); + NSUInteger numberOfSyncsByThisFunction = [functionNamesCounter countForObject:functionNameStr]; + NSArray *stack = [NSThread callStackSymbols]; + GTMSESSION_ASSERT_DEBUG(numberOfSyncsByThisFunction > 0, @"Sync not found on %@ at %s\n%@", + [_objectKey.nonretainedObjectValue class], _functionName, + [stack subarrayWithRange:NSMakeRange(1, stack.count - 1)]); + [functionNamesCounter removeObject:functionNameStr]; + if (functionNamesCounter.count == 0) { + [counters removeObjectForKey:_objectKey]; + } +} + ++ (NSArray *)functionsHoldingSynchronizationOnObject:(id)object { + Class threadKey = [GTMSessionSyncMonitorInternal class]; + NSValue *localObjectKey = [NSValue valueWithNonretainedObject:object]; + + NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary; + NSMutableDictionary *counters = threadDict[threadKey]; + NSCountedSet *functionNamesCounter = counters[localObjectKey]; + return functionNamesCounter.count > 0 ? functionNamesCounter.allObjects : nil; +} +@end +#endif // DEBUG && (!defined(NS_BLOCK_ASSERTIONS) || GTMSESSION_ASSERT_AS_LOG) +GTM_ASSUME_NONNULL_END diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.h b/Pods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.h @@ -0,0 +1,112 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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 "GTMSessionFetcher.h" + +// GTM HTTP Logging +// +// All traffic using GTMSessionFetcher can be easily logged. Call +// +// [GTMSessionFetcher setLoggingEnabled:YES]; +// +// to begin generating log files. +// +// Unless explicitly set by the application using +setLoggingDirectory:, +// logs are put into a default directory, located at: +// * macOS: ~/Desktop/GTMHTTPDebugLogs +// * iOS simulator: ~/GTMHTTPDebugLogs (in application sandbox) +// * iOS device: ~/Documents/GTMHTTPDebugLogs (in application sandbox) +// +// Tip: use the Finder's "Sort By Date" to find the most recent logs. +// +// Each run of an application gets a separate set of log files. An html +// file is generated to simplify browsing the run's http transactions. +// The html file includes javascript links for inline viewing of uploaded +// and downloaded data. +// +// A symlink is created in the logs folder to simplify finding the html file +// for the latest run of the application; the symlink is called +// +// AppName_http_log_newest.html +// +// For better viewing of XML logs, use Camino or Firefox rather than Safari. +// +// Each fetcher may be given a comment to be inserted as a label in the logs, +// such as +// [fetcher setCommentWithFormat:@"retrieve item %@", itemName]; +// +// Projects may define STRIP_GTM_FETCH_LOGGING to remove logging code. + +#if !STRIP_GTM_FETCH_LOGGING + +@interface GTMSessionFetcher (GTMSessionFetcherLogging) + +// Note: on macOS the default logs directory is ~/Desktop/GTMHTTPDebugLogs; on +// iOS simulators it will be the ~/GTMHTTPDebugLogs (in the app sandbox); on +// iOS devices it will be in ~/Documents/GTMHTTPDebugLogs (in the app sandbox). +// These directories will be created as needed, and are excluded from backups +// to iCloud and iTunes. +// +// If a custom directory is set, the directory should already exist. It is +// the application's responsibility to exclude any custom directory from +// backups, if desired. ++ (void)setLoggingDirectory:(NSString *)path; ++ (NSString *)loggingDirectory; + +// client apps can turn logging on and off ++ (void)setLoggingEnabled:(BOOL)isLoggingEnabled; ++ (BOOL)isLoggingEnabled; + +// client apps can turn off logging to a file if they want to only check +// the fetcher's log property ++ (void)setLoggingToFileEnabled:(BOOL)isLoggingToFileEnabled; ++ (BOOL)isLoggingToFileEnabled; + +// client apps can optionally specify process name and date string used in +// log file names ++ (void)setLoggingProcessName:(NSString *)processName; ++ (NSString *)loggingProcessName; + ++ (void)setLoggingDateStamp:(NSString *)dateStamp; ++ (NSString *)loggingDateStamp; + +// client apps can specify the directory for the log for this specific run, +// typically to match the directory used by another fetcher class, like: +// +// [GTMSessionFetcher setLogDirectoryForCurrentRun:[GTMHTTPFetcher logDirectoryForCurrentRun]]; +// +// Setting this overrides the logging directory, process name, and date stamp when writing +// the log file. ++ (void)setLogDirectoryForCurrentRun:(NSString *)logDirectoryForCurrentRun; ++ (NSString *)logDirectoryForCurrentRun; + +// Prunes old log directories that have not been modified since the provided date. +// This will not delete the current run's log directory. ++ (void)deleteLogDirectoriesOlderThanDate:(NSDate *)date; + +// internal; called by fetcher +- (void)logFetchWithError:(NSError *)error; +- (NSInputStream *)loggedInputStreamForInputStream:(NSInputStream *)inputStream; +- (GTMSessionFetcherBodyStreamProvider)loggedStreamProviderForStreamProvider: + (GTMSessionFetcherBodyStreamProvider)streamProvider; + +// internal; accessors useful for viewing logs ++ (NSString *)processNameLogPrefix; ++ (NSString *)symlinkNameSuffix; ++ (NSString *)htmlFileName; + +@end + +#endif // !STRIP_GTM_FETCH_LOGGING diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.m b/Pods/GTMSessionFetcher/Source/GTMSessionFetcherLogging.m @@ -0,0 +1,982 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#include <sys/stat.h> +#include <unistd.h> + +#import "GTMSessionFetcherLogging.h" + +#ifndef STRIP_GTM_FETCH_LOGGING + #error GTMSessionFetcher headers should have defaulted this if it wasn't already defined. +#endif + +#if !STRIP_GTM_FETCH_LOGGING + +// Sensitive credential strings are replaced in logs with _snip_ +// +// Apps that must see the contents of sensitive tokens can set this to 1 +#ifndef SKIP_GTM_FETCH_LOGGING_SNIPPING +#define SKIP_GTM_FETCH_LOGGING_SNIPPING 0 +#endif + +// If GTMReadMonitorInputStream is available, it can be used for +// capturing uploaded streams of data +// +// We locally declare methods of GTMReadMonitorInputStream so we +// do not need to import the header, as some projects may not have it available +#if !GTMSESSION_BUILD_COMBINED_SOURCES +@interface GTMReadMonitorInputStream : NSInputStream + ++ (instancetype)inputStreamWithStream:(NSInputStream *)input; + +@property (assign) id readDelegate; +@property (assign) SEL readSelector; + +@end +#else +@class GTMReadMonitorInputStream; +#endif // !GTMSESSION_BUILD_COMBINED_SOURCES + +@interface GTMSessionFetcher (GTMHTTPFetcherLoggingUtilities) + ++ (NSString *)headersStringForDictionary:(NSDictionary *)dict; ++ (NSString *)snipSubstringOfString:(NSString *)originalStr + betweenStartString:(NSString *)startStr + endString:(NSString *)endStr; +- (void)inputStream:(GTMReadMonitorInputStream *)stream + readIntoBuffer:(void *)buffer + length:(int64_t)length; + +@end + +@implementation GTMSessionFetcher (GTMSessionFetcherLogging) + +// fetchers come and fetchers go, but statics are forever +static BOOL gIsLoggingEnabled = NO; +static BOOL gIsLoggingToFile = YES; +static NSString *gLoggingDirectoryPath = nil; +static NSString *gLogDirectoryForCurrentRun = nil; +static NSString *gLoggingDateStamp = nil; +static NSString *gLoggingProcessName = nil; + ++ (void)setLoggingDirectory:(NSString *)path { + gLoggingDirectoryPath = [path copy]; +} + ++ (NSString *)loggingDirectory { + if (!gLoggingDirectoryPath) { + NSArray *paths = nil; +#if TARGET_IPHONE_SIMULATOR + // default to a directory called GTMHTTPDebugLogs into a sandbox-safe + // directory that a developer can find easily, the application home + paths = @[ NSHomeDirectory() ]; +#elif TARGET_OS_IPHONE + // Neither ~/Desktop nor ~/Home is writable on an actual iOS, watchOS, or tvOS device. + // Put it in ~/Documents. + paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); +#else + // default to a directory called GTMHTTPDebugLogs in the desktop folder + paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES); +#endif + + NSString *desktopPath = paths.firstObject; + if (desktopPath) { + NSString *const kGTMLogFolderName = @"GTMHTTPDebugLogs"; + NSString *logsFolderPath = [desktopPath stringByAppendingPathComponent:kGTMLogFolderName]; + + NSFileManager *fileMgr = [NSFileManager defaultManager]; + BOOL isDir; + BOOL doesFolderExist = [fileMgr fileExistsAtPath:logsFolderPath isDirectory:&isDir]; + if (!doesFolderExist) { + // make the directory + doesFolderExist = [fileMgr createDirectoryAtPath:logsFolderPath + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + if (doesFolderExist) { + // The directory has been created. Exclude it from backups. + NSURL *pathURL = [NSURL fileURLWithPath:logsFolderPath isDirectory:YES]; + [pathURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL]; + } + } + + if (doesFolderExist) { + // it's there; store it in the global + gLoggingDirectoryPath = [logsFolderPath copy]; + } + } + } + return gLoggingDirectoryPath; +} + ++ (void)setLogDirectoryForCurrentRun:(NSString *)logDirectoryForCurrentRun { + // Set the path for this run's logs. + gLogDirectoryForCurrentRun = [logDirectoryForCurrentRun copy]; +} + ++ (NSString *)logDirectoryForCurrentRun { + // make a directory for this run's logs, like SyncProto_logs_10-16_01-56-58PM + if (gLogDirectoryForCurrentRun) return gLogDirectoryForCurrentRun; + + NSString *parentDir = [self loggingDirectory]; + NSString *logNamePrefix = [self processNameLogPrefix]; + NSString *dateStamp = [self loggingDateStamp]; + NSString *dirName = [NSString stringWithFormat:@"%@%@", logNamePrefix, dateStamp]; + NSString *logDirectory = [parentDir stringByAppendingPathComponent:dirName]; + + if (gIsLoggingToFile) { + NSFileManager *fileMgr = [NSFileManager defaultManager]; + // Be sure that the first time this app runs, it's not writing to a preexisting folder + static BOOL gShouldReuseFolder = NO; + if (!gShouldReuseFolder) { + gShouldReuseFolder = YES; + NSString *origLogDir = logDirectory; + for (int ctr = 2; ctr < 20; ++ctr) { + if (![fileMgr fileExistsAtPath:logDirectory]) break; + + // append a digit + logDirectory = [origLogDir stringByAppendingFormat:@"_%d", ctr]; + } + } + if (![fileMgr createDirectoryAtPath:logDirectory + withIntermediateDirectories:YES + attributes:nil + error:NULL]) return nil; + } + gLogDirectoryForCurrentRun = logDirectory; + + return gLogDirectoryForCurrentRun; +} + ++ (void)setLoggingEnabled:(BOOL)isLoggingEnabled { + gIsLoggingEnabled = isLoggingEnabled; +} + ++ (BOOL)isLoggingEnabled { + return gIsLoggingEnabled; +} + ++ (void)setLoggingToFileEnabled:(BOOL)isLoggingToFileEnabled { + gIsLoggingToFile = isLoggingToFileEnabled; +} + ++ (BOOL)isLoggingToFileEnabled { + return gIsLoggingToFile; +} + ++ (void)setLoggingProcessName:(NSString *)processName { + gLoggingProcessName = [processName copy]; +} + ++ (NSString *)loggingProcessName { + // get the process name (once per run) replacing spaces with underscores + if (!gLoggingProcessName) { + NSString *procName = [[NSProcessInfo processInfo] processName]; + gLoggingProcessName = [procName stringByReplacingOccurrencesOfString:@" " withString:@"_"]; + } + return gLoggingProcessName; +} + ++ (void)setLoggingDateStamp:(NSString *)dateStamp { + gLoggingDateStamp = [dateStamp copy]; +} + ++ (NSString *)loggingDateStamp { + // We'll pick one date stamp per run, so a run that starts at a later second + // will get a unique results html file + if (!gLoggingDateStamp) { + // produce a string like 08-21_01-41-23PM + + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setFormatterBehavior:NSDateFormatterBehavior10_4]; + [formatter setDateFormat:@"M-dd_hh-mm-ssa"]; + + gLoggingDateStamp = [formatter stringFromDate:[NSDate date]]; + } + return gLoggingDateStamp; +} + ++ (NSString *)processNameLogPrefix { + static NSString *gPrefix = nil; + if (!gPrefix) { + NSString *processName = [self loggingProcessName]; + gPrefix = [[NSString alloc] initWithFormat:@"%@_log_", processName]; + } + return gPrefix; +} + ++ (NSString *)symlinkNameSuffix { + return @"_log_newest.html"; +} + ++ (NSString *)htmlFileName { + return @"aperçu_http_log.html"; +} + ++ (void)deleteLogDirectoriesOlderThanDate:(NSDate *)cutoffDate { + NSFileManager *fileMgr = [NSFileManager defaultManager]; + NSURL *parentDir = [NSURL fileURLWithPath:[[self class] loggingDirectory]]; + NSURL *logDirectoryForCurrentRun = + [NSURL fileURLWithPath:[[self class] logDirectoryForCurrentRun]]; + NSError *error; + NSArray *contents = [fileMgr contentsOfDirectoryAtURL:parentDir + includingPropertiesForKeys:@[ NSURLContentModificationDateKey ] + options:0 + error:&error]; + for (NSURL *itemURL in contents) { + if ([itemURL isEqual:logDirectoryForCurrentRun]) continue; + + NSDate *modDate; + if ([itemURL getResourceValue:&modDate + forKey:NSURLContentModificationDateKey + error:&error]) { + if ([modDate compare:cutoffDate] == NSOrderedAscending) { + if (![fileMgr removeItemAtURL:itemURL error:&error]) { + NSLog(@"deleteLogDirectoriesOlderThanDate failed to delete %@: %@", + itemURL.path, error); + } + } + } else { + NSLog(@"deleteLogDirectoriesOlderThanDate failed to get mod date of %@: %@", + itemURL.path, error); + } + } +} + +// formattedStringFromData returns a prettyprinted string for XML or JSON input, +// and a plain string for other input data +- (NSString *)formattedStringFromData:(NSData *)inputData + contentType:(NSString *)contentType + JSON:(NSDictionary **)outJSON { + if (!inputData) return nil; + + // if the content type is JSON and we have the parsing class available, use that + if ([contentType hasPrefix:@"application/json"] && inputData.length > 5) { + // convert from JSON string to NSObjects and back to a formatted string + NSMutableDictionary *obj = [NSJSONSerialization JSONObjectWithData:inputData + options:NSJSONReadingMutableContainers + error:NULL]; + if (obj) { + if (outJSON) *outJSON = obj; + if ([obj isKindOfClass:[NSMutableDictionary class]]) { + // for security and privacy, omit OAuth 2 response access and refresh tokens + if ([obj valueForKey:@"refresh_token"] != nil) { + [obj setObject:@"_snip_" forKey:@"refresh_token"]; + } + if ([obj valueForKey:@"access_token"] != nil) { + [obj setObject:@"_snip_" forKey:@"access_token"]; + } + } + NSData *data = [NSJSONSerialization dataWithJSONObject:obj + options:NSJSONWritingPrettyPrinted + error:NULL]; + if (data) { + NSString *jsonStr = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + return jsonStr; + } + } + } + +#if !TARGET_OS_IPHONE && !GTM_SKIP_LOG_XMLFORMAT + // verify that this data starts with the bytes indicating XML + + NSString *const kXMLLintPath = @"/usr/bin/xmllint"; + static BOOL gHasCheckedAvailability = NO; + static BOOL gIsXMLLintAvailable = NO; + + if (!gHasCheckedAvailability) { + gIsXMLLintAvailable = [[NSFileManager defaultManager] fileExistsAtPath:kXMLLintPath]; + gHasCheckedAvailability = YES; + } + if (gIsXMLLintAvailable + && inputData.length > 5 + && strncmp(inputData.bytes, "<?xml", 5) == 0) { + + // call xmllint to format the data + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:kXMLLintPath]; + + // use the dash argument to specify stdin as the source file + [task setArguments:@[ @"--format", @"-" ]]; + [task setEnvironment:@{}]; + + NSPipe *inputPipe = [NSPipe pipe]; + NSPipe *outputPipe = [NSPipe pipe]; + [task setStandardInput:inputPipe]; + [task setStandardOutput:outputPipe]; + + [task launch]; + + [[inputPipe fileHandleForWriting] writeData:inputData]; + [[inputPipe fileHandleForWriting] closeFile]; + + // drain the stdout before waiting for the task to exit + NSData *formattedData = [[outputPipe fileHandleForReading] readDataToEndOfFile]; + + [task waitUntilExit]; + + int status = [task terminationStatus]; + if (status == 0 && formattedData.length > 0) { + // success + inputData = formattedData; + } + } +#else + // we can't call external tasks on the iPhone; leave the XML unformatted +#endif + + NSString *dataStr = [[NSString alloc] initWithData:inputData + encoding:NSUTF8StringEncoding]; + return dataStr; +} + +// stringFromStreamData creates a string given the supplied data +// +// If NSString can create a UTF-8 string from the data, then that is returned. +// +// Otherwise, this routine tries to find a MIME boundary at the beginning of the data block, and +// uses that to break up the data into parts. Each part will be used to try to make a UTF-8 string. +// For parts that fail, a replacement string showing the part header and <<n bytes>> is supplied +// in place of the binary data. + +- (NSString *)stringFromStreamData:(NSData *)data + contentType:(NSString *)contentType { + + if (!data) return nil; + + // optimistically, see if the whole data block is UTF-8 + NSString *streamDataStr = [self formattedStringFromData:data + contentType:contentType + JSON:NULL]; + if (streamDataStr) return streamDataStr; + + // Munge a buffer by replacing non-ASCII bytes with underscores, and turn that munged buffer an + // NSString. That gives us a string we can use with NSScanner. + NSMutableData *mutableData = [NSMutableData dataWithData:data]; + unsigned char *bytes = (unsigned char *)mutableData.mutableBytes; + + for (unsigned int idx = 0; idx < mutableData.length; ++idx) { + if (bytes[idx] > 0x7F || bytes[idx] == 0) { + bytes[idx] = '_'; + } + } + + NSString *mungedStr = [[NSString alloc] initWithData:mutableData + encoding:NSUTF8StringEncoding]; + if (mungedStr) { + + // scan for the boundary string + NSString *boundary = nil; + NSScanner *scanner = [NSScanner scannerWithString:mungedStr]; + + if ([scanner scanUpToString:@"\r\n" intoString:&boundary] + && [boundary hasPrefix:@"--"]) { + + // we found a boundary string; use it to divide the string into parts + NSArray *mungedParts = [mungedStr componentsSeparatedByString:boundary]; + + // look at each munged part in the original string, and try to convert those into UTF-8 + NSMutableArray *origParts = [NSMutableArray array]; + NSUInteger offset = 0; + for (NSString *mungedPart in mungedParts) { + NSUInteger partSize = mungedPart.length; + NSData *origPartData = [data subdataWithRange:NSMakeRange(offset, partSize)]; + NSString *origPartStr = [[NSString alloc] initWithData:origPartData + encoding:NSUTF8StringEncoding]; + if (origPartStr) { + // we could make this original part into UTF-8; use the string + [origParts addObject:origPartStr]; + } else { + // this part can't be made into UTF-8; scan the header, if we can + NSString *header = nil; + NSScanner *headerScanner = [NSScanner scannerWithString:mungedPart]; + if (![headerScanner scanUpToString:@"\r\n\r\n" intoString:&header]) { + // we couldn't find a header + header = @""; + } + // make a part string with the header and <<n bytes>> + NSString *binStr = [NSString stringWithFormat:@"\r%@\r<<%lu bytes>>\r", + header, (long)(partSize - header.length)]; + [origParts addObject:binStr]; + } + offset += partSize + boundary.length; + } + // rejoin the original parts + streamDataStr = [origParts componentsJoinedByString:boundary]; + } + } + if (!streamDataStr) { + // give up; just make a string showing the uploaded bytes + streamDataStr = [NSString stringWithFormat:@"<<%u bytes>>", (unsigned int)data.length]; + } + return streamDataStr; +} + +// logFetchWithError is called following a successful or failed fetch attempt +// +// This method does all the work for appending to and creating log files + +- (void)logFetchWithError:(NSError *)error { + if (![[self class] isLoggingEnabled]) return; + NSString *logDirectory = [[self class] logDirectoryForCurrentRun]; + if (!logDirectory) return; + NSString *processName = [[self class] loggingProcessName]; + + // TODO: add Javascript to display response data formatted in hex + + // each response's NSData goes into its own xml or txt file, though all responses for this run of + // the app share a main html file. This counter tracks all fetch responses for this app run. + // + // we'll use a local variable since this routine may be reentered while waiting for XML formatting + // to be completed by an external task + static int gResponseCounter = 0; + int responseCounter = ++gResponseCounter; + + NSURLResponse *response = [self response]; + NSDictionary *responseHeaders = [self responseHeaders]; + NSString *responseDataStr = nil; + NSDictionary *responseJSON = nil; + + // if there's response data, decide what kind of file to put it in based on the first bytes of the + // file or on the mime type supplied by the server + NSString *responseMIMEType = [response MIMEType]; + BOOL isResponseImage = NO; + + // file name for an image data file + NSString *responseDataFileName = nil; + + int64_t responseDataLength = self.downloadedLength; + if (responseDataLength > 0) { + NSData *downloadedData = self.downloadedData; + if (downloadedData == nil + && responseDataLength > 0 + && responseDataLength < 20000 + && self.destinationFileURL) { + // There's a download file that's not too big, so get the data to display from the downloaded + // file. + NSURL *destinationURL = self.destinationFileURL; + downloadedData = [NSData dataWithContentsOfURL:destinationURL]; + } + NSString *responseType = [responseHeaders valueForKey:@"Content-Type"]; + responseDataStr = [self formattedStringFromData:downloadedData + contentType:responseType + JSON:&responseJSON]; + NSString *responseDataExtn = nil; + NSData *dataToWrite = nil; + if (responseDataStr) { + // we were able to make a UTF-8 string from the response data + if ([responseMIMEType isEqual:@"application/atom+xml"] + || [responseMIMEType hasSuffix:@"/xml"]) { + responseDataExtn = @"xml"; + dataToWrite = [responseDataStr dataUsingEncoding:NSUTF8StringEncoding]; + } + } else if ([responseMIMEType isEqual:@"image/jpeg"]) { + responseDataExtn = @"jpg"; + dataToWrite = downloadedData; + isResponseImage = YES; + } else if ([responseMIMEType isEqual:@"image/gif"]) { + responseDataExtn = @"gif"; + dataToWrite = downloadedData; + isResponseImage = YES; + } else if ([responseMIMEType isEqual:@"image/png"]) { + responseDataExtn = @"png"; + dataToWrite = downloadedData; + isResponseImage = YES; + } else { + // add more non-text types here + } + // if we have an extension, save the raw data in a file with that extension + if (responseDataExtn && dataToWrite) { + // generate a response file base name like + NSString *responseBaseName = [NSString stringWithFormat:@"fetch_%d_response", responseCounter]; + responseDataFileName = [responseBaseName stringByAppendingPathExtension:responseDataExtn]; + NSString *responseDataFilePath = [logDirectory stringByAppendingPathComponent:responseDataFileName]; + + NSError *downloadedError = nil; + if (gIsLoggingToFile && ![dataToWrite writeToFile:responseDataFilePath + options:0 + error:&downloadedError]) { + NSLog(@"%@ logging write error:%@ (%@)", [self class], downloadedError, responseDataFileName); + } + } + } + // we'll have one main html file per run of the app + NSString *htmlName = [[self class] htmlFileName]; + NSString *htmlPath =[logDirectory stringByAppendingPathComponent:htmlName]; + + // if the html file exists (from logging previous fetches) we don't need + // to re-write the header or the scripts + NSFileManager *fileMgr = [NSFileManager defaultManager]; + BOOL didFileExist = [fileMgr fileExistsAtPath:htmlPath]; + + NSMutableString* outputHTML = [NSMutableString string]; + + // we need a header to say we'll have UTF-8 text + if (!didFileExist) { + [outputHTML appendFormat:@"<html><head><meta http-equiv=\"content-type\" " + "content=\"text/html; charset=UTF-8\"><title>%@ HTTP fetch log %@</title>", + processName, [[self class] loggingDateStamp]]; + } + // now write the visible html elements + NSString *copyableFileName = [NSString stringWithFormat:@"fetch_%d.txt", responseCounter]; + + NSDate *now = [NSDate date]; + // write the date & time, the comment, and the link to the plain-text (copyable) log + [outputHTML appendFormat:@"<b>%@ &nbsp;&nbsp;&nbsp;&nbsp; ", now]; + + NSString *comment = [self comment]; + if (comment.length > 0) { + [outputHTML appendFormat:@"%@ &nbsp;&nbsp;&nbsp;&nbsp; ", comment]; + } + [outputHTML appendFormat:@"</b><a href='%@'><i>request/response log</i></a><br>", copyableFileName]; + NSTimeInterval elapsed = -self.initialBeginFetchDate.timeIntervalSinceNow; + [outputHTML appendFormat:@"elapsed: %5.3fsec<br>", elapsed]; + + // write the request URL + NSURLRequest *request = self.request; + NSString *requestMethod = request.HTTPMethod; + NSURL *requestURL = request.URL; + + // Save the request URL for next time in case this redirects. + NSString *redirectedFromURLString = [self.redirectedFromURL absoluteString]; + self.redirectedFromURL = [requestURL copy]; + if (redirectedFromURLString) { + [outputHTML appendFormat:@"<FONT COLOR='#990066'><i>redirected from %@</i></FONT><br>", + redirectedFromURLString]; + } + [outputHTML appendFormat:@"<b>request:</b> %@ <code>%@</code><br>\n", requestMethod, requestURL]; + + // write the request headers + NSDictionary *requestHeaders = request.allHTTPHeaderFields; + NSUInteger numberOfRequestHeaders = requestHeaders.count; + if (numberOfRequestHeaders > 0) { + // Indicate if the request is authorized; warn if the request is authorized but non-SSL + NSString *auth = [requestHeaders objectForKey:@"Authorization"]; + NSString *headerDetails = @""; + if (auth) { + BOOL isInsecure = [[requestURL scheme] isEqual:@"http"]; + if (isInsecure) { + // 26A0 = ⚠ + headerDetails = + @"&nbsp;&nbsp;&nbsp;<i>authorized, non-SSL</i><FONT COLOR='#FF00FF'> &#x26A0;</FONT> "; + } else { + headerDetails = @"&nbsp;&nbsp;&nbsp;<i>authorized</i>"; + } + } + NSString *cookiesHdr = [requestHeaders objectForKey:@"Cookie"]; + if (cookiesHdr) { + headerDetails = [headerDetails stringByAppendingString:@"&nbsp;&nbsp;&nbsp;<i>cookies</i>"]; + } + NSString *matchHdr = [requestHeaders objectForKey:@"If-Match"]; + if (matchHdr) { + headerDetails = [headerDetails stringByAppendingString:@"&nbsp;&nbsp;&nbsp;<i>if-match</i>"]; + } + matchHdr = [requestHeaders objectForKey:@"If-None-Match"]; + if (matchHdr) { + headerDetails = [headerDetails stringByAppendingString:@"&nbsp;&nbsp;&nbsp;<i>if-none-match</i>"]; + } + [outputHTML appendFormat:@"&nbsp;&nbsp; headers: %d %@<br>", + (int)numberOfRequestHeaders, headerDetails]; + } else { + [outputHTML appendFormat:@"&nbsp;&nbsp; headers: none<br>"]; + } + // write the request post data + NSData *bodyData = nil; + NSData *loggedStreamData = self.loggedStreamData; + if (loggedStreamData) { + bodyData = loggedStreamData; + } else { + bodyData = self.bodyData; + if (bodyData == nil) { + bodyData = self.request.HTTPBody; + } + } + uint64_t bodyDataLength = bodyData.length; + + if (bodyData.length == 0) { + // If the data is in a body upload file URL, read that in if it's not huge. + NSURL *bodyFileURL = self.bodyFileURL; + if (bodyFileURL) { + NSNumber *fileSizeNum = nil; + NSError *fileSizeError = nil; + if ([bodyFileURL getResourceValue:&fileSizeNum + forKey:NSURLFileSizeKey + error:&fileSizeError]) { + bodyDataLength = [fileSizeNum unsignedLongLongValue]; + if (bodyDataLength > 0 && bodyDataLength < 50000) { + bodyData = [NSData dataWithContentsOfURL:bodyFileURL + options:NSDataReadingUncached + error:&fileSizeError]; + } + } + } + } + NSString *bodyDataStr = nil; + NSString *postType = [requestHeaders valueForKey:@"Content-Type"]; + + if (bodyDataLength > 0) { + [outputHTML appendFormat:@"&nbsp;&nbsp; data: %llu bytes, <code>%@</code><br>\n", + bodyDataLength, postType ? postType : @"(no type)"]; + NSString *logRequestBody = self.logRequestBody; + if (logRequestBody) { + bodyDataStr = [logRequestBody copy]; + self.logRequestBody = nil; + } else { + bodyDataStr = [self stringFromStreamData:bodyData + contentType:postType]; + if (bodyDataStr) { + // remove OAuth 2 client secret and refresh token + bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr + betweenStartString:@"client_secret=" + endString:@"&"]; + bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr + betweenStartString:@"refresh_token=" + endString:@"&"]; + // remove ClientLogin password + bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr + betweenStartString:@"&Passwd=" + endString:@"&"]; + } + } + } else { + // no post data + } + // write the response status, MIME type, URL + NSInteger status = [self statusCode]; + if (response) { + NSString *statusString = @""; + if (status != 0) { + if (status == 200 || status == 201) { + statusString = [NSString stringWithFormat:@"%ld", (long)status]; + + // report any JSON-RPC error + if ([responseJSON isKindOfClass:[NSDictionary class]]) { + NSDictionary *jsonError = [responseJSON objectForKey:@"error"]; + if ([jsonError isKindOfClass:[NSDictionary class]]) { + NSString *jsonCode = [[jsonError valueForKey:@"code"] description]; + NSString *jsonMessage = [jsonError valueForKey:@"message"]; + if (jsonCode || jsonMessage) { + // 2691 = ⚑ + NSString *const jsonErrFmt = + @"&nbsp;&nbsp;&nbsp;<i>JSON error:</i> <FONT COLOR='#FF00FF'>%@ %@ &nbsp;&#x2691;</FONT>"; + statusString = [statusString stringByAppendingFormat:jsonErrFmt, + jsonCode ? jsonCode : @"", + jsonMessage ? jsonMessage : @""]; + } + } + } + } else { + // purple for anything other than 200 or 201 + NSString *flag = status >= 400 ? @"&nbsp;&#x2691;" : @""; // 2691 = ⚑ + NSString *explanation = [NSHTTPURLResponse localizedStringForStatusCode:status]; + NSString *const statusFormat = @"<FONT COLOR='#FF00FF'>%ld %@ %@</FONT>"; + statusString = [NSString stringWithFormat:statusFormat, (long)status, explanation, flag]; + } + } + // show the response URL only if it's different from the request URL + NSString *responseURLStr = @""; + NSURL *responseURL = response.URL; + + if (responseURL && ![responseURL isEqual:request.URL]) { + NSString *const responseURLFormat = + @"<FONT COLOR='#FF00FF'>response URL:</FONT> <code>%@</code><br>\n"; + responseURLStr = [NSString stringWithFormat:responseURLFormat, [responseURL absoluteString]]; + } + [outputHTML appendFormat:@"<b>response:</b>&nbsp;&nbsp;status %@<br>\n%@", + statusString, responseURLStr]; + // Write the response headers + NSUInteger numberOfResponseHeaders = responseHeaders.count; + if (numberOfResponseHeaders > 0) { + // Indicate if the server is setting cookies + NSString *cookiesSet = [responseHeaders valueForKey:@"Set-Cookie"]; + NSString *cookiesStr = + cookiesSet ? @"&nbsp;&nbsp;<FONT COLOR='#990066'><i>sets cookies</i></FONT>" : @""; + // Indicate if the server is redirecting + NSString *location = [responseHeaders valueForKey:@"Location"]; + BOOL isRedirect = status >= 300 && status <= 399 && location != nil; + NSString *redirectsStr = + isRedirect ? @"&nbsp;&nbsp;<FONT COLOR='#990066'><i>redirects</i></FONT>" : @""; + [outputHTML appendFormat:@"&nbsp;&nbsp; headers: %d %@ %@<br>\n", + (int)numberOfResponseHeaders, cookiesStr, redirectsStr]; + } else { + [outputHTML appendString:@"&nbsp;&nbsp; headers: none<br>\n"]; + } + } + // error + if (error) { + [outputHTML appendFormat:@"<b>Error:</b> %@ <br>\n", error.description]; + } + // Write the response data + if (responseDataFileName) { + if (isResponseImage) { + // Make a small inline image that links to the full image file + [outputHTML appendFormat:@"&nbsp;&nbsp; data: %lld bytes, <code>%@</code><br>", + responseDataLength, responseMIMEType]; + NSString *const fmt = + @"<a href=\"%@\"><img src='%@' alt='image' style='border:solid thin;max-height:32'></a>\n"; + [outputHTML appendFormat:fmt, responseDataFileName, responseDataFileName]; + } else { + // The response data was XML; link to the xml file + NSString *const fmt = + @"&nbsp;&nbsp; data: %lld bytes, <code>%@</code>&nbsp;&nbsp;&nbsp;<i><a href=\"%@\">%@</a></i>\n"; + [outputHTML appendFormat:fmt, responseDataLength, responseMIMEType, + responseDataFileName, [responseDataFileName pathExtension]]; + } + } else { + // The response data was not an image; just show the length and MIME type + [outputHTML appendFormat:@"&nbsp;&nbsp; data: %lld bytes, <code>%@</code>\n", + responseDataLength, responseMIMEType ? responseMIMEType : @"(no response type)"]; + } + // Make a single string of the request and response, suitable for copying + // to the clipboard and pasting into a bug report + NSMutableString *copyable = [NSMutableString string]; + if (comment) { + [copyable appendFormat:@"%@\n\n", comment]; + } + [copyable appendFormat:@"%@ elapsed: %5.3fsec\n", now, elapsed]; + if (redirectedFromURLString) { + [copyable appendFormat:@"Redirected from %@\n", redirectedFromURLString]; + } + [copyable appendFormat:@"Request: %@ %@\n", requestMethod, requestURL]; + if (requestHeaders.count > 0) { + [copyable appendFormat:@"Request headers:\n%@\n", + [[self class] headersStringForDictionary:requestHeaders]]; + } + if (bodyDataLength > 0) { + [copyable appendFormat:@"Request body: (%llu bytes)\n", bodyDataLength]; + if (bodyDataStr) { + [copyable appendFormat:@"%@\n", bodyDataStr]; + } + [copyable appendString:@"\n"]; + } + if (response) { + [copyable appendFormat:@"Response: status %d\n", (int) status]; + [copyable appendFormat:@"Response headers:\n%@\n", + [[self class] headersStringForDictionary:responseHeaders]]; + [copyable appendFormat:@"Response body: (%lld bytes)\n", responseDataLength]; + if (responseDataLength > 0) { + NSString *logResponseBody = self.logResponseBody; + if (logResponseBody) { + // The user has provided the response body text. + responseDataStr = [logResponseBody copy]; + self.logResponseBody = nil; + } + if (responseDataStr != nil) { + [copyable appendFormat:@"%@\n", responseDataStr]; + } else { + // Even though it's redundant, we'll put in text to indicate that all the bytes are binary. + if (self.destinationFileURL) { + [copyable appendFormat:@"<<%lld bytes>> to file %@\n", + responseDataLength, self.destinationFileURL.path]; + } else { + [copyable appendFormat:@"<<%lld bytes>>\n", responseDataLength]; + } + } + } + } + if (error) { + [copyable appendFormat:@"Error: %@\n", error]; + } + // Save to log property before adding the separator + self.log = copyable; + + [copyable appendString:@"-----------------------------------------------------------\n"]; + + // Write the copyable version to another file (linked to at the top of the html file, above) + // + // Ideally, something to just copy this to the clipboard like + // <span onCopy='window.event.clipboardData.setData(\"Text\", + // \"copyable stuff\");return false;'>Copy here.</span>" + // would work everywhere, but it only works in Safari as of 8/2010 + if (gIsLoggingToFile) { + NSString *parentDir = [[self class] loggingDirectory]; + NSString *copyablePath = [logDirectory stringByAppendingPathComponent:copyableFileName]; + NSError *copyableError = nil; + if (![copyable writeToFile:copyablePath + atomically:NO + encoding:NSUTF8StringEncoding + error:&copyableError]) { + // Error writing to file + NSLog(@"%@ logging write error:%@ (%@)", [self class], copyableError, copyablePath); + } + [outputHTML appendString:@"<br><hr><p>"]; + + // Append the HTML to the main output file + const char* htmlBytes = outputHTML.UTF8String; + NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:htmlPath + append:YES]; + [stream open]; + [stream write:(const uint8_t *) htmlBytes maxLength:strlen(htmlBytes)]; + [stream close]; + + // Make a symlink to the latest html + NSString *const symlinkNameSuffix = [[self class] symlinkNameSuffix]; + NSString *symlinkName = [processName stringByAppendingString:symlinkNameSuffix]; + NSString *symlinkPath = [parentDir stringByAppendingPathComponent:symlinkName]; + + [fileMgr removeItemAtPath:symlinkPath error:NULL]; + [fileMgr createSymbolicLinkAtPath:symlinkPath + withDestinationPath:htmlPath + error:NULL]; +#if TARGET_OS_IPHONE + static BOOL gReportedLoggingPath = NO; + if (!gReportedLoggingPath) { + gReportedLoggingPath = YES; + NSLog(@"GTMSessionFetcher logging to \"%@\"", parentDir); + } +#endif + } +} + +- (NSInputStream *)loggedInputStreamForInputStream:(NSInputStream *)inputStream { + if (!inputStream) return nil; + if (![GTMSessionFetcher isLoggingEnabled]) return inputStream; + + [self clearLoggedStreamData]; // Clear any previous data. + Class monitorClass = NSClassFromString(@"GTMReadMonitorInputStream"); + if (!monitorClass) { + NSString const *str = @"<<Uploaded stream log unavailable without GTMReadMonitorInputStream>>"; + NSData *stringData = [str dataUsingEncoding:NSUTF8StringEncoding]; + [self appendLoggedStreamData:stringData]; + return inputStream; + } + inputStream = [monitorClass inputStreamWithStream:inputStream]; + + GTMReadMonitorInputStream *readMonitorInputStream = (GTMReadMonitorInputStream *)inputStream; + [readMonitorInputStream setReadDelegate:self]; + SEL readSel = @selector(inputStream:readIntoBuffer:length:); + [readMonitorInputStream setReadSelector:readSel]; + + return inputStream; +} + +- (GTMSessionFetcherBodyStreamProvider)loggedStreamProviderForStreamProvider: + (GTMSessionFetcherBodyStreamProvider)streamProvider { + if (!streamProvider) return nil; + if (![GTMSessionFetcher isLoggingEnabled]) return streamProvider; + + [self clearLoggedStreamData]; // Clear any previous data. + Class monitorClass = NSClassFromString(@"GTMReadMonitorInputStream"); + if (!monitorClass) { + NSString const *str = @"<<Uploaded stream log unavailable without GTMReadMonitorInputStream>>"; + NSData *stringData = [str dataUsingEncoding:NSUTF8StringEncoding]; + [self appendLoggedStreamData:stringData]; + return streamProvider; + } + GTMSessionFetcherBodyStreamProvider loggedStreamProvider = + ^(GTMSessionFetcherBodyStreamProviderResponse response) { + streamProvider(^(NSInputStream *bodyStream) { + bodyStream = [self loggedInputStreamForInputStream:bodyStream]; + response(bodyStream); + }); + }; + return loggedStreamProvider; +} + +@end + +@implementation GTMSessionFetcher (GTMSessionFetcherLoggingUtilities) + +- (void)inputStream:(GTMReadMonitorInputStream *)stream + readIntoBuffer:(void *)buffer + length:(int64_t)length { + // append the captured data + NSData *data = [NSData dataWithBytesNoCopy:buffer + length:(NSUInteger)length + freeWhenDone:NO]; + [self appendLoggedStreamData:data]; +} + +#pragma mark Fomatting Utilities + ++ (NSString *)snipSubstringOfString:(NSString *)originalStr + betweenStartString:(NSString *)startStr + endString:(NSString *)endStr { +#if SKIP_GTM_FETCH_LOGGING_SNIPPING + return originalStr; +#else + if (!originalStr) return nil; + + // Find the start string, and replace everything between it + // and the end string (or the end of the original string) with "_snip_" + NSRange startRange = [originalStr rangeOfString:startStr]; + if (startRange.location == NSNotFound) return originalStr; + + // We found the start string + NSUInteger originalLength = originalStr.length; + NSUInteger startOfTarget = NSMaxRange(startRange); + NSRange targetAndRest = NSMakeRange(startOfTarget, originalLength - startOfTarget); + NSRange endRange = [originalStr rangeOfString:endStr + options:0 + range:targetAndRest]; + NSRange replaceRange; + if (endRange.location == NSNotFound) { + // Found no end marker so replace to end of string + replaceRange = targetAndRest; + } else { + // Replace up to the endStr + replaceRange = NSMakeRange(startOfTarget, endRange.location - startOfTarget); + } + NSString *result = [originalStr stringByReplacingCharactersInRange:replaceRange + withString:@"_snip_"]; + return result; +#endif // SKIP_GTM_FETCH_LOGGING_SNIPPING +} + ++ (NSString *)headersStringForDictionary:(NSDictionary *)dict { + // Format the dictionary in http header style, like + // Accept: application/json + // Cache-Control: no-cache + // Content-Type: application/json; charset=utf-8 + // + // Pad the key names, but not beyond 16 chars, since long custom header + // keys just create too much whitespace + NSArray *keys = [dict.allKeys sortedArrayUsingSelector:@selector(compare:)]; + + NSMutableString *str = [NSMutableString string]; + for (NSString *key in keys) { + NSString *value = [dict valueForKey:key]; + if ([key isEqual:@"Authorization"]) { + // Remove OAuth 1 token + value = [[self class] snipSubstringOfString:value + betweenStartString:@"oauth_token=\"" + endString:@"\""]; + + // Remove OAuth 2 bearer token (draft 16, and older form) + value = [[self class] snipSubstringOfString:value + betweenStartString:@"Bearer " + endString:@"\n"]; + value = [[self class] snipSubstringOfString:value + betweenStartString:@"OAuth " + endString:@"\n"]; + + // Remove Google ClientLogin + value = [[self class] snipSubstringOfString:value + betweenStartString:@"GoogleLogin auth=" + endString:@"\n"]; + } + [str appendFormat:@" %@: %@\n", key, value]; + } + return str; +} + +@end + +#endif // !STRIP_GTM_FETCH_LOGGING diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.h b/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.h @@ -0,0 +1,193 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +// For best performance and convenient usage, fetchers should be generated by a common +// GTMSessionFetcherService instance, like +// +// _fetcherService = [[GTMSessionFetcherService alloc] init]; +// GTMSessionFetcher* myFirstFetcher = [_fetcherService fetcherWithRequest:request1]; +// GTMSessionFetcher* mySecondFetcher = [_fetcherService fetcherWithRequest:request2]; + +#import "GTMSessionFetcher.h" + +GTM_ASSUME_NONNULL_BEGIN + +// Notifications. + +// This notification indicates a reusable session has become invalid. It is intended mainly for the +// service's unit tests. +// +// The notification object is the fetcher service. +// The invalid session is provided via the userInfo kGTMSessionFetcherServiceSessionKey key. +extern NSString *const kGTMSessionFetcherServiceSessionBecameInvalidNotification; +extern NSString *const kGTMSessionFetcherServiceSessionKey; + +@interface GTMSessionFetcherService : NSObject<GTMSessionFetcherServiceProtocol> + +// Queues of delayed and running fetchers. Each dictionary contains arrays +// of GTMSessionFetcher *fetchers, keyed by NSString *host +@property(atomic, strong, readonly, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSArray *) *delayedFetchersByHost; +@property(atomic, strong, readonly, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSArray *) *runningFetchersByHost; + +// A max value of 0 means no fetchers should be delayed. +// The default limit is 10 simultaneous fetchers targeting each host. +// This does not apply to fetchers whose useBackgroundSession property is YES. Since services are +// not resurrected on an app relaunch, delayed fetchers would effectively be abandoned. +@property(atomic, assign) NSUInteger maxRunningFetchersPerHost; + +// Properties to be applied to each fetcher; see GTMSessionFetcher.h for descriptions +@property(atomic, strong, GTM_NULLABLE) NSURLSessionConfiguration *configuration; +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherConfigurationBlock configurationBlock; +@property(atomic, strong, GTM_NULLABLE) NSHTTPCookieStorage *cookieStorage; +@property(atomic, strong, GTM_NULL_RESETTABLE) dispatch_queue_t callbackQueue; +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherChallengeBlock challengeBlock; +@property(atomic, strong, GTM_NULLABLE) NSURLCredential *credential; +@property(atomic, strong) NSURLCredential *proxyCredential; +@property(atomic, copy, GTM_NULLABLE) GTM_NSArrayOf(NSString *) *allowedInsecureSchemes; +@property(atomic, assign) BOOL allowLocalhostRequest; +@property(atomic, assign) BOOL allowInvalidServerCertificates; +@property(atomic, assign, getter=isRetryEnabled) BOOL retryEnabled; +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherRetryBlock retryBlock; +@property(atomic, assign) NSTimeInterval maxRetryInterval; +@property(atomic, assign) NSTimeInterval minRetryInterval; +@property(atomic, copy, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, id) *properties; + +#if GTM_BACKGROUND_TASK_FETCHING +@property(atomic, assign) BOOL skipBackgroundTask; +#endif + +// A default useragent of GTMFetcherStandardUserAgentString(nil) will be given to each fetcher +// created by this service unless the request already has a user-agent header set. +// This default will be added starting with builds with the SDKs for OS X 10.11 and iOS 9. +// +// To use the configuration's default user agent, set this property to nil. +@property(atomic, copy, GTM_NULLABLE) NSString *userAgent; + +// The authorizer to attach to the created fetchers. If a specific fetcher should +// not authorize its requests, the fetcher's authorizer property may be set to nil +// before the fetch begins. +@property(atomic, strong, GTM_NULLABLE) id<GTMFetcherAuthorizationProtocol> authorizer; + +// Delegate queue used by the session when calling back to the fetcher. The default +// is the main queue. Changing this does not affect the queue used to call back to the +// application; that is specified by the callbackQueue property above. +@property(atomic, strong, GTM_NULL_RESETTABLE) NSOperationQueue *sessionDelegateQueue; + +// When enabled, indicates the same session should be used by subsequent fetchers. +// +// This is enabled by default. +@property(atomic, assign) BOOL reuseSession; + +// Sets the delay until an unused session is invalidated. +// The default interval is 60 seconds. +// +// If the interval is set to 0, then any reused session is not invalidated except by +// explicitly invoking -resetSession. Be aware that setting the interval to 0 thus +// causes the session's delegate to be retained until the session is explicitly reset. +@property(atomic, assign) NSTimeInterval unusedSessionTimeout; + +// If shouldReuseSession is enabled, this will force creation of a new session when future +// fetchers begin. +- (void)resetSession; + +// Create a fetcher +// +// These methods will return a fetcher. If successfully created, the connection +// will hold a strong reference to it for the life of the connection as well. +// So the caller doesn't have to hold onto the fetcher explicitly unless they +// want to be able to monitor or cancel it. +- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request; +- (GTMSessionFetcher *)fetcherWithURL:(NSURL *)requestURL; +- (GTMSessionFetcher *)fetcherWithURLString:(NSString *)requestURLString; + +// Common method for fetcher creation. +// +// -fetcherWithRequest:fetcherClass: may be overridden to customize creation of +// fetchers. This is the ONLY method in the GTMSessionFetcher library intended to +// be overridden. +- (id)fetcherWithRequest:(NSURLRequest *)request + fetcherClass:(Class)fetcherClass; + +- (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher; + +- (NSUInteger)numberOfFetchers; // running + delayed fetchers +- (NSUInteger)numberOfRunningFetchers; +- (NSUInteger)numberOfDelayedFetchers; + +// Return a list of all running or delayed fetchers. This includes fetchers created +// by the service which have been started and have not yet stopped. +// +// Returns an array of fetcher objects, or nil if none. +- (GTM_NULLABLE GTM_NSArrayOf(GTMSessionFetcher *) *)issuedFetchers; + +// Search for running or delayed fetchers with the specified URL. +// +// Returns an array of fetcher objects found, or nil if none found. +- (GTM_NULLABLE GTM_NSArrayOf(GTMSessionFetcher *) *)issuedFetchersWithRequestURL:(NSURL *)requestURL; + +- (void)stopAllFetchers; + +// Methods for use by the fetcher class only. +- (GTM_NULLABLE NSURLSession *)session; +- (GTM_NULLABLE NSURLSession *)sessionForFetcherCreation; +- (GTM_NULLABLE id<NSURLSessionDelegate>)sessionDelegate; +- (GTM_NULLABLE NSDate *)stoppedAllFetchersDate; + +// The testBlock can inspect its fetcher parameter's request property to +// determine which fetcher is being faked. +@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherTestBlock testBlock; + +@end + +@interface GTMSessionFetcherService (TestingSupport) + +// Convenience methods to create a fetcher service for testing. +// +// Fetchers generated by this mock fetcher service will not perform any +// network operation, but will invoke callbacks and provide the supplied data +// or error to the completion handler. +// +// You can make more customized mocks by setting the test block property of the service +// or fetcher; the test block can inspect the fetcher's request or other properties. +// +// See the description of the testBlock property below. ++ (instancetype)mockFetcherServiceWithFakedData:(GTM_NULLABLE NSData *)fakedDataOrNil + fakedError:(GTM_NULLABLE NSError *)fakedErrorOrNil; ++ (instancetype)mockFetcherServiceWithFakedData:(GTM_NULLABLE NSData *)fakedDataOrNil + fakedResponse:(NSHTTPURLResponse *)fakedResponse + fakedError:(GTM_NULLABLE NSError *)fakedErrorOrNil; + +// Spin the run loop and discard events (or, if not on the main thread, just sleep the thread) +// until all running and delayed fetchers have completed. +// +// This is only for use in testing or in tools without a user interface. +// +// Synchronous fetches should never be done by shipping apps; they are +// sufficient reason for rejection from the app store. +// +// Returns NO if timed out. +- (BOOL)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds; + +@end + +@interface GTMSessionFetcherService (BackwardsCompatibilityOnly) + +// Clients using GTMSessionFetcher should set the cookie storage explicitly themselves. +// This method is just for compatibility with the old fetcher. +@property(atomic, assign) NSInteger cookieStorageMethod; + +@end + +GTM_ASSUME_NONNULL_END diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.m b/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.m @@ -0,0 +1,1365 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMSessionFetcherService.h" + +NSString *const kGTMSessionFetcherServiceSessionBecameInvalidNotification + = @"kGTMSessionFetcherServiceSessionBecameInvalidNotification"; +NSString *const kGTMSessionFetcherServiceSessionKey + = @"kGTMSessionFetcherServiceSessionKey"; + +#if !GTMSESSION_BUILD_COMBINED_SOURCES +@interface GTMSessionFetcher (ServiceMethods) +- (BOOL)beginFetchMayDelay:(BOOL)mayDelay + mayAuthorize:(BOOL)mayAuthorize; +@end +#endif // !GTMSESSION_BUILD_COMBINED_SOURCES + +@interface GTMSessionFetcherService () + +@property(atomic, strong, readwrite) NSDictionary *delayedFetchersByHost; +@property(atomic, strong, readwrite) NSDictionary *runningFetchersByHost; + +@end + +// Since NSURLSession doesn't support a separate delegate per task (!), instances of this +// class serve as a session delegate trampoline. +// +// This class maps a session's tasks to fetchers, and resends delegate messages to the task's +// fetcher. +@interface GTMSessionFetcherSessionDelegateDispatcher : NSObject<NSURLSessionDelegate> + +// The session for the tasks in this dispatcher's task-to-fetcher map. +@property(atomic) NSURLSession *session; + +// The timer interval for invalidating a session that has no active tasks. +@property(atomic) NSTimeInterval discardInterval; + +// The current discard timer. +@property(atomic, readonly) NSTimer *discardTimer; + + +- (instancetype)initWithParentService:(GTMSessionFetcherService *)parentService + sessionDiscardInterval:(NSTimeInterval)discardInterval; + +- (void)setFetcher:(GTMSessionFetcher *)fetcher + forTask:(NSURLSessionTask *)task; +- (void)removeFetcher:(GTMSessionFetcher *)fetcher; + +// Before using a session, tells the delegate dispatcher to stop the discard timer. +- (void)startSessionUsage; + +// When abandoning a delegate dispatcher, we want to avoid the session retaining +// the delegate after tasks complete. +- (void)abandon; + +@end + + +@implementation GTMSessionFetcherService { + NSMutableDictionary *_delayedFetchersByHost; + NSMutableDictionary *_runningFetchersByHost; + NSUInteger _maxRunningFetchersPerHost; + + // When this ivar is nil, the service will not reuse sessions. + GTMSessionFetcherSessionDelegateDispatcher *_delegateDispatcher; + + // Fetchers will wait on this if another fetcher is creating the shared NSURLSession. + dispatch_semaphore_t _sessionCreationSemaphore; + + dispatch_queue_t _callbackQueue; + NSOperationQueue *_delegateQueue; + NSHTTPCookieStorage *_cookieStorage; + NSString *_userAgent; + NSTimeInterval _timeout; + + NSURLCredential *_credential; // Username & password. + NSURLCredential *_proxyCredential; // Credential supplied to proxy servers. + + NSInteger _cookieStorageMethod; + + id<GTMFetcherAuthorizationProtocol> _authorizer; + + // For waitForCompletionOfAllFetchersWithTimeout: we need to wait on stopped fetchers since + // they've not yet finished invoking their queued callbacks. This array is nil except when + // waiting on fetchers. + NSMutableArray *_stoppedFetchersToWaitFor; + + // For fetchers that enqueued their callbacks before stopAllFetchers was called on the service, + // set a barrier so the callbacks know to bail out. + NSDate *_stoppedAllFetchersDate; +} + +@synthesize maxRunningFetchersPerHost = _maxRunningFetchersPerHost, + configuration = _configuration, + configurationBlock = _configurationBlock, + cookieStorage = _cookieStorage, + userAgent = _userAgent, + challengeBlock = _challengeBlock, + credential = _credential, + proxyCredential = _proxyCredential, + allowedInsecureSchemes = _allowedInsecureSchemes, + allowLocalhostRequest = _allowLocalhostRequest, + allowInvalidServerCertificates = _allowInvalidServerCertificates, + retryEnabled = _retryEnabled, + retryBlock = _retryBlock, + maxRetryInterval = _maxRetryInterval, + minRetryInterval = _minRetryInterval, + properties = _properties, + unusedSessionTimeout = _unusedSessionTimeout, + testBlock = _testBlock; + +#if GTM_BACKGROUND_TASK_FETCHING +@synthesize skipBackgroundTask = _skipBackgroundTask; +#endif + +- (instancetype)init { + self = [super init]; + if (self) { + _delayedFetchersByHost = [[NSMutableDictionary alloc] init]; + _runningFetchersByHost = [[NSMutableDictionary alloc] init]; + _maxRunningFetchersPerHost = 10; + _cookieStorageMethod = -1; + _unusedSessionTimeout = 60.0; + _delegateDispatcher = + [[GTMSessionFetcherSessionDelegateDispatcher alloc] initWithParentService:self + sessionDiscardInterval:_unusedSessionTimeout]; + _callbackQueue = dispatch_get_main_queue(); + + _delegateQueue = [[NSOperationQueue alloc] init]; + _delegateQueue.maxConcurrentOperationCount = 1; + _delegateQueue.name = @"com.google.GTMSessionFetcher.NSURLSessionDelegateQueue"; + + _sessionCreationSemaphore = dispatch_semaphore_create(1); + + // Starting with the SDKs for OS X 10.11/iOS 9, the service has a default useragent. + // Apps can remove this and get the default system "CFNetwork" useragent by setting the + // fetcher service's userAgent property to nil. +#if (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \ + || (TARGET_OS_IPHONE && defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0) + _userAgent = GTMFetcherStandardUserAgentString(nil); +#endif + } + return self; +} + +- (void)dealloc { + [self detachAuthorizer]; + [_delegateDispatcher abandon]; +} + +#pragma mark Generate a new fetcher + +// Clients may override this method. Clients should not override any other library methods. +- (id)fetcherWithRequest:(NSURLRequest *)request + fetcherClass:(Class)fetcherClass { + GTMSessionFetcher *fetcher = [[fetcherClass alloc] initWithRequest:request + configuration:self.configuration]; + fetcher.callbackQueue = self.callbackQueue; + fetcher.sessionDelegateQueue = self.sessionDelegateQueue; + fetcher.challengeBlock = self.challengeBlock; + fetcher.credential = self.credential; + fetcher.proxyCredential = self.proxyCredential; + fetcher.authorizer = self.authorizer; + fetcher.cookieStorage = self.cookieStorage; + fetcher.allowedInsecureSchemes = self.allowedInsecureSchemes; + fetcher.allowLocalhostRequest = self.allowLocalhostRequest; + fetcher.allowInvalidServerCertificates = self.allowInvalidServerCertificates; + fetcher.configurationBlock = self.configurationBlock; + fetcher.retryEnabled = self.retryEnabled; + fetcher.retryBlock = self.retryBlock; + fetcher.maxRetryInterval = self.maxRetryInterval; + fetcher.minRetryInterval = self.minRetryInterval; + fetcher.properties = self.properties; + fetcher.service = self; + if (self.cookieStorageMethod >= 0) { + [fetcher setCookieStorageMethod:self.cookieStorageMethod]; + } + +#if GTM_BACKGROUND_TASK_FETCHING + fetcher.skipBackgroundTask = self.skipBackgroundTask; +#endif + + NSString *userAgent = self.userAgent; + if (userAgent.length > 0 + && [request valueForHTTPHeaderField:@"User-Agent"] == nil) { + [fetcher setRequestValue:userAgent + forHTTPHeaderField:@"User-Agent"]; + } + fetcher.testBlock = self.testBlock; + + return fetcher; +} + +- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request { + return [self fetcherWithRequest:request + fetcherClass:[GTMSessionFetcher class]]; +} + +- (GTMSessionFetcher *)fetcherWithURL:(NSURL *)requestURL { + return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]]; +} + +- (GTMSessionFetcher *)fetcherWithURLString:(NSString *)requestURLString { + NSURL *url = [NSURL URLWithString:requestURLString]; + return [self fetcherWithURL:url]; +} + +// Returns a session for the fetcher's host, or nil. +- (NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSURLSession *session = _delegateDispatcher.session; + return session; + } +} + +// Returns a session for the fetcher's host, or nil. For shared sessions, this +// waits on a semaphore, blocking other fetchers while the caller creates the +// session if needed. +- (NSURLSession *)sessionForFetcherCreation { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + if (!_delegateDispatcher) { + // This fetcher is creating a non-shared session, so skip the semaphore usage. + return nil; + } + } + + // Wait if another fetcher is currently creating a session; avoid waiting + // inside the @synchronized block, as that can deadlock. + dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER); + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Before getting the NSURLSession for task creation, it is + // important to invalidate and nil out the session discard timer; otherwise + // the session can be invalidated between when it is returned to the + // fetcher, and when the fetcher attempts to create its NSURLSessionTask. + [_delegateDispatcher startSessionUsage]; + + NSURLSession *session = _delegateDispatcher.session; + if (session) { + // The calling fetcher will receive a preexisting session, so + // we can allow other fetchers to create a session. + dispatch_semaphore_signal(_sessionCreationSemaphore); + } else { + // No existing session was obtained, so the calling fetcher will create the session; + // it *must* invoke fetcherDidCreateSession: to signal the dispatcher's semaphore after + // the session has been created (or fails to be created) to avoid a hang. + } + return session; + } +} + +- (id<NSURLSessionDelegate>)sessionDelegate { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _delegateDispatcher; + } +} + +#pragma mark Queue Management + +- (void)addRunningFetcher:(GTMSessionFetcher *)fetcher + forHost:(NSString *)host { + // Add to the array of running fetchers for this host, creating the array if needed. + NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host]; + if (runningForHost == nil) { + runningForHost = [NSMutableArray arrayWithObject:fetcher]; + [_runningFetchersByHost setObject:runningForHost forKey:host]; + } else { + [runningForHost addObject:fetcher]; + } +} + +- (void)addDelayedFetcher:(GTMSessionFetcher *)fetcher + forHost:(NSString *)host { + // Add to the array of delayed fetchers for this host, creating the array if needed. + NSMutableArray *delayedForHost = [_delayedFetchersByHost objectForKey:host]; + if (delayedForHost == nil) { + delayedForHost = [NSMutableArray arrayWithObject:fetcher]; + [_delayedFetchersByHost setObject:delayedForHost forKey:host]; + } else { + [delayedForHost addObject:fetcher]; + } +} + +- (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSString *host = fetcher.request.URL.host; + if (host == nil) { + return NO; + } + NSArray *delayedForHost = [_delayedFetchersByHost objectForKey:host]; + NSUInteger idx = [delayedForHost indexOfObjectIdenticalTo:fetcher]; + BOOL isDelayed = (delayedForHost != nil) && (idx != NSNotFound); + return isDelayed; + } +} + +- (BOOL)fetcherShouldBeginFetching:(GTMSessionFetcher *)fetcher { + // Entry point from the fetcher + NSURL *requestURL = fetcher.request.URL; + NSString *host = requestURL.host; + + // Addresses "file:///path" case where localhost is the implicit host. + if (host.length == 0 && [requestURL isFileURL]) { + host = @"localhost"; + } + + if (host.length == 0) { + // Data URIs legitimately have no host, reject other hostless URLs. + GTMSESSION_ASSERT_DEBUG([[requestURL scheme] isEqual:@"data"], @"%@ lacks host", fetcher); + return YES; + } + + BOOL shouldBeginResult; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host]; + if (runningForHost != nil + && [runningForHost indexOfObjectIdenticalTo:fetcher] != NSNotFound) { + GTMSESSION_ASSERT_DEBUG(NO, @"%@ was already running", fetcher); + return YES; + } + + BOOL shouldRunNow = (fetcher.usingBackgroundSession + || _maxRunningFetchersPerHost == 0 + || _maxRunningFetchersPerHost > + [[self class] numberOfNonBackgroundSessionFetchers:runningForHost]); + if (shouldRunNow) { + [self addRunningFetcher:fetcher forHost:host]; + shouldBeginResult = YES; + } else { + [self addDelayedFetcher:fetcher forHost:host]; + shouldBeginResult = NO; + } + } // @synchronized(self) + + // We'll save the host that serves as the key for this fetcher's array + // to avoid any chance of the underlying request changing, stranding + // the fetcher in the wrong array + fetcher.serviceHost = host; + + return shouldBeginResult; +} + +- (void)startFetcher:(GTMSessionFetcher *)fetcher { + [fetcher beginFetchMayDelay:NO + mayAuthorize:YES]; +} + +// Internal utility. Returns a fetcher's delegate if it's a dispatcher, or nil if the fetcher +// is its own delegate and has no dispatcher. +- (GTMSessionFetcherSessionDelegateDispatcher *)delegateDispatcherForFetcher:(GTMSessionFetcher *)fetcher { + GTMSessionCheckNotSynchronized(self); + + NSURLSession *fetcherSession = fetcher.session; + if (fetcherSession) { + id<NSURLSessionDelegate> fetcherDelegate = fetcherSession.delegate; + BOOL hasDispatcher = (fetcherDelegate != nil && fetcherDelegate != fetcher); + if (hasDispatcher) { + GTMSESSION_ASSERT_DEBUG([fetcherDelegate isKindOfClass:[GTMSessionFetcherSessionDelegateDispatcher class]], + @"Fetcher delegate class: %@", [fetcherDelegate class]); + return (GTMSessionFetcherSessionDelegateDispatcher *)fetcherDelegate; + } + } + return nil; +} + +- (void)fetcherDidCreateSession:(GTMSessionFetcher *)fetcher { + if (fetcher.canShareSession) { + NSURLSession *fetcherSession = fetcher.session; + GTMSESSION_ASSERT_DEBUG(fetcherSession != nil, @"Fetcher missing its session: %@", fetcher); + + GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher = + [self delegateDispatcherForFetcher:fetcher]; + if (delegateDispatcher) { + GTMSESSION_ASSERT_DEBUG(delegateDispatcher.session == nil, + @"Fetcher made an extra session: %@", fetcher); + + // Save this fetcher's session. + delegateDispatcher.session = fetcherSession; + + // Allow other fetchers to request this session now. + dispatch_semaphore_signal(_sessionCreationSemaphore); + } + } +} + +- (void)fetcherDidBeginFetching:(GTMSessionFetcher *)fetcher { + // If this fetcher has a separate delegate with a shared session, then + // this fetcher should be added to the delegate's map of tasks to fetchers. + GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher = + [self delegateDispatcherForFetcher:fetcher]; + if (delegateDispatcher) { + GTMSESSION_ASSERT_DEBUG(fetcher.canShareSession, + @"Inappropriate shared session: %@", fetcher); + + // There should already be a session, from this or a previous fetcher. + // + // Sanity check that the fetcher's session is the delegate's shared session. + NSURLSession *sharedSession = delegateDispatcher.session; + NSURLSession *fetcherSession = fetcher.session; + GTMSESSION_ASSERT_DEBUG(sharedSession != nil, @"Missing delegate session: %@", fetcher); + GTMSESSION_ASSERT_DEBUG(fetcherSession == sharedSession, + @"Inconsistent session: %@ %@ (shared: %@)", + fetcher, fetcherSession, sharedSession); + + if (sharedSession != nil && fetcherSession == sharedSession) { + NSURLSessionTask *task = fetcher.sessionTask; + GTMSESSION_ASSERT_DEBUG(task != nil, @"Missing session task: %@", fetcher); + + if (task) { + [delegateDispatcher setFetcher:fetcher + forTask:task]; + } + } + } +} + +- (void)stopFetcher:(GTMSessionFetcher *)fetcher { + [fetcher stopFetching]; +} + +- (void)fetcherDidStop:(GTMSessionFetcher *)fetcher { + // Entry point from the fetcher + NSString *host = fetcher.serviceHost; + if (!host) { + // fetcher has been stopped previously + return; + } + + // This removeFetcher: invocation is a fallback; typically, fetchers are removed from the task + // map when the task completes. + GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher = + [self delegateDispatcherForFetcher:fetcher]; + [delegateDispatcher removeFetcher:fetcher]; + + NSMutableArray *fetchersToStart; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // If a test is waiting for all fetchers to stop, it needs to wait for this one + // to invoke its callbacks on the callback queue. + [_stoppedFetchersToWaitFor addObject:fetcher]; + + NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host]; + [runningForHost removeObject:fetcher]; + + NSMutableArray *delayedForHost = [_delayedFetchersByHost objectForKey:host]; + [delayedForHost removeObject:fetcher]; + + while (delayedForHost.count > 0 + && [[self class] numberOfNonBackgroundSessionFetchers:runningForHost] + < _maxRunningFetchersPerHost) { + // Start another delayed fetcher running, scanning for the minimum + // priority value, defaulting to FIFO for equal priorities + GTMSessionFetcher *nextFetcher = nil; + for (GTMSessionFetcher *delayedFetcher in delayedForHost) { + if (nextFetcher == nil + || delayedFetcher.servicePriority < nextFetcher.servicePriority) { + nextFetcher = delayedFetcher; + } + } + + if (nextFetcher) { + [self addRunningFetcher:nextFetcher forHost:host]; + runningForHost = [_runningFetchersByHost objectForKey:host]; + + [delayedForHost removeObjectIdenticalTo:nextFetcher]; + + if (!fetchersToStart) { + fetchersToStart = [NSMutableArray array]; + } + [fetchersToStart addObject:nextFetcher]; + } + } + + if (runningForHost.count == 0) { + // None left; remove the empty array + [_runningFetchersByHost removeObjectForKey:host]; + } + + if (delayedForHost.count == 0) { + [_delayedFetchersByHost removeObjectForKey:host]; + } + } // @synchronized(self) + + // Start fetchers outside of the synchronized block to avoid a deadlock. + for (GTMSessionFetcher *nextFetcher in fetchersToStart) { + [self startFetcher:nextFetcher]; + } + + // The fetcher is no longer in the running or the delayed array, + // so remove its host and thread properties + fetcher.serviceHost = nil; +} + +- (NSUInteger)numberOfFetchers { + NSUInteger running = [self numberOfRunningFetchers]; + NSUInteger delayed = [self numberOfDelayedFetchers]; + return running + delayed; +} + +- (NSUInteger)numberOfRunningFetchers { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSUInteger sum = 0; + for (NSString *host in _runningFetchersByHost) { + NSArray *fetchers = [_runningFetchersByHost objectForKey:host]; + sum += fetchers.count; + } + return sum; + } +} + +- (NSUInteger)numberOfDelayedFetchers { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSUInteger sum = 0; + for (NSString *host in _delayedFetchersByHost) { + NSArray *fetchers = [_delayedFetchersByHost objectForKey:host]; + sum += fetchers.count; + } + return sum; + } +} + +- (NSArray *)issuedFetchers { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSMutableArray *allFetchers = [NSMutableArray array]; + void (^accumulateFetchers)(id, id, BOOL *) = ^(NSString *host, + NSArray *fetchersForHost, + BOOL *stop) { + [allFetchers addObjectsFromArray:fetchersForHost]; + }; + [_runningFetchersByHost enumerateKeysAndObjectsUsingBlock:accumulateFetchers]; + [_delayedFetchersByHost enumerateKeysAndObjectsUsingBlock:accumulateFetchers]; + + GTMSESSION_ASSERT_DEBUG(allFetchers.count == [NSSet setWithArray:allFetchers].count, + @"Fetcher appears multiple times\n running: %@\n delayed: %@", + _runningFetchersByHost, _delayedFetchersByHost); + + return allFetchers.count > 0 ? allFetchers : nil; + } +} + +- (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL { + NSString *host = requestURL.host; + if (host.length == 0) return nil; + + NSURL *targetURL = [requestURL absoluteURL]; + + NSArray *allFetchers = [self issuedFetchers]; + NSIndexSet *indexes = [allFetchers indexesOfObjectsPassingTest:^BOOL(GTMSessionFetcher *fetcher, + NSUInteger idx, + BOOL *stop) { + NSURL *fetcherURL = [fetcher.request.URL absoluteURL]; + return [fetcherURL isEqual:targetURL]; + }]; + + NSArray *result = nil; + if (indexes.count > 0) { + result = [allFetchers objectsAtIndexes:indexes]; + } + return result; +} + +- (void)stopAllFetchers { + NSArray *delayedFetchersByHost; + NSArray *runningFetchersByHost; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Set the time barrier so fetchers know not to call back even if + // the stop calls below occur after the fetchers naturally + // stopped and so were removed from _runningFetchersByHost, + // but while the callbacks were already enqueued before stopAllFetchers + // was invoked. + _stoppedAllFetchersDate = [[NSDate alloc] init]; + + // Remove fetchers from the delayed list to avoid fetcherDidStop: from + // starting more fetchers running as a side effect of stopping one + delayedFetchersByHost = _delayedFetchersByHost.allValues; + [_delayedFetchersByHost removeAllObjects]; + + runningFetchersByHost = _runningFetchersByHost.allValues; + [_runningFetchersByHost removeAllObjects]; + } + + for (NSArray *delayedForHost in delayedFetchersByHost) { + for (GTMSessionFetcher *fetcher in delayedForHost) { + [self stopFetcher:fetcher]; + } + } + + for (NSArray *runningForHost in runningFetchersByHost) { + for (GTMSessionFetcher *fetcher in runningForHost) { + [self stopFetcher:fetcher]; + } + } +} + +- (NSDate *)stoppedAllFetchersDate { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _stoppedAllFetchersDate; + } +} + +#pragma mark Accessors + +- (BOOL)reuseSession { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _delegateDispatcher != nil; + } +} + +- (void)setReuseSession:(BOOL)shouldReuse { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + BOOL wasReusing = (_delegateDispatcher != nil); + if (shouldReuse != wasReusing) { + [self abandonDispatcher]; + if (shouldReuse) { + _delegateDispatcher = + [[GTMSessionFetcherSessionDelegateDispatcher alloc] initWithParentService:self + sessionDiscardInterval:_unusedSessionTimeout]; + } else { + _delegateDispatcher = nil; + } + } + } +} + +- (void)resetSession { + GTMSessionCheckNotSynchronized(self); + dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER); + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + [self resetSessionInternal]; + } + + dispatch_semaphore_signal(_sessionCreationSemaphore); +} + +- (void)resetSessionInternal { + GTMSessionCheckSynchronized(self); + + // The old dispatchers may be retained as delegates of any ongoing sessions by those sessions. + if (_delegateDispatcher) { + [self abandonDispatcher]; + _delegateDispatcher = + [[GTMSessionFetcherSessionDelegateDispatcher alloc] initWithParentService:self + sessionDiscardInterval:_unusedSessionTimeout]; + } +} + +- (void)resetSessionForDispatcherDiscardTimer:(NSTimer *)timer { + GTMSessionCheckNotSynchronized(self); + + dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER); + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_delegateDispatcher.discardTimer == timer) { + // If the delegate dispatcher's current discardTimer is the same object as the timer + // that fired, no fetcher has recently attempted to start using the session by calling + // startSessionUsage, which invalidates and nils out the timer. + [self resetSessionInternal]; + } else { + // A fetcher has invalidated the timer between its triggering and now, potentially + // meaning a fetcher has requested access to the NSURLSession, and may be in the process + // of starting a new task. The dispatcher should not be abandoned, as this can lead + // to a race condition between calling -finishTasksAndInvalidate on the NSURLSession + // and the fetcher attempting to create a new task. + } + } + + dispatch_semaphore_signal(_sessionCreationSemaphore); +} + +- (NSTimeInterval)unusedSessionTimeout { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _unusedSessionTimeout; + } +} + +- (void)setUnusedSessionTimeout:(NSTimeInterval)timeout { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _unusedSessionTimeout = timeout; + _delegateDispatcher.discardInterval = timeout; + } +} + +// This method should be called inside of @synchronized(self) +- (void)abandonDispatcher { + GTMSessionCheckSynchronized(self); + [_delegateDispatcher abandon]; +} + +- (NSDictionary *)runningFetchersByHost { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_runningFetchersByHost copy]; + } +} + +- (void)setRunningFetchersByHost:(NSDictionary *)dict { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _runningFetchersByHost = [dict mutableCopy]; + } +} + +- (NSDictionary *)delayedFetchersByHost { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_delayedFetchersByHost copy]; + } +} + +- (void)setDelayedFetchersByHost:(NSDictionary *)dict { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _delayedFetchersByHost = [dict mutableCopy]; + } +} + +- (id<GTMFetcherAuthorizationProtocol>)authorizer { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _authorizer; + } +} + +- (void)setAuthorizer:(id<GTMFetcherAuthorizationProtocol>)obj { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (obj != _authorizer) { + [self detachAuthorizer]; + } + + _authorizer = obj; + } + + // Use the fetcher service for the authorization fetches if the auth + // object supports fetcher services + if ([obj respondsToSelector:@selector(setFetcherService:)]) { +#if GTM_USE_SESSION_FETCHER + [obj setFetcherService:self]; +#else + [obj setFetcherService:(id)self]; +#endif + } +} + +// This should be called inside a @synchronized(self) block except during dealloc. +- (void)detachAuthorizer { + // This method is called by the fetcher service's dealloc and setAuthorizer: + // methods; do not override. + // + // The fetcher service retains the authorizer, and the authorizer has a + // weak pointer to the fetcher service (a non-zeroing pointer for + // compatibility with iOS 4 and Mac OS X 10.5/10.6.) + // + // When this fetcher service no longer uses the authorizer, we want to remove + // the authorizer's dependence on the fetcher service. Authorizers can still + // function without a fetcher service. + if ([_authorizer respondsToSelector:@selector(fetcherService)]) { + id authFetcherService = [_authorizer fetcherService]; + if (authFetcherService == self) { + [_authorizer setFetcherService:nil]; + } + } +} + +- (dispatch_queue_t GTM_NONNULL_TYPE)callbackQueue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _callbackQueue; + } // @synchronized(self) +} + +- (void)setCallbackQueue:(dispatch_queue_t GTM_NULLABLE_TYPE)queue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _callbackQueue = queue ?: dispatch_get_main_queue(); + } // @synchronized(self) +} + +- (NSOperationQueue * GTM_NONNULL_TYPE)sessionDelegateQueue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _delegateQueue; + } // @synchronized(self) +} + +- (void)setSessionDelegateQueue:(NSOperationQueue * GTM_NULLABLE_TYPE)queue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _delegateQueue = queue ?: [NSOperationQueue mainQueue]; + } // @synchronized(self) +} + +- (NSOperationQueue *)delegateQueue { + // Provided for compatibility with the old fetcher service. The gtm-oauth2 code respects + // any custom delegate queue for calling the app. + return nil; +} + ++ (NSUInteger)numberOfNonBackgroundSessionFetchers:(NSArray *)fetchers { + NSUInteger sum = 0; + for (GTMSessionFetcher *fetcher in fetchers) { + if (!fetcher.usingBackgroundSession) { + ++sum; + } + } + return sum; +} + +@end + +@implementation GTMSessionFetcherService (TestingSupport) + ++ (instancetype)mockFetcherServiceWithFakedData:(NSData *)fakedDataOrNil + fakedError:(NSError *)fakedErrorOrNil { +#if !GTM_DISABLE_FETCHER_TEST_BLOCK + NSURL *url = [NSURL URLWithString:@"http://example.invalid"]; + NSHTTPURLResponse *fakedResponse = + [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:(fakedErrorOrNil ? 500 : 200) + HTTPVersion:@"HTTP/1.1" + headerFields:nil]; + return [self mockFetcherServiceWithFakedData:fakedDataOrNil + fakedResponse:fakedResponse + fakedError:fakedErrorOrNil]; +#else + GTMSESSION_ASSERT_DEBUG(0, @"Test blocks disabled"); + return nil; +#endif // GTM_DISABLE_FETCHER_TEST_BLOCK +} + ++ (instancetype)mockFetcherServiceWithFakedData:(NSData *)fakedDataOrNil + fakedResponse:(NSHTTPURLResponse *)fakedResponse + fakedError:(NSError *)fakedErrorOrNil { +#if !GTM_DISABLE_FETCHER_TEST_BLOCK + GTMSessionFetcherService *service = [[self alloc] init]; + service.allowedInsecureSchemes = @[ @"http" ]; + service.testBlock = ^(GTMSessionFetcher *fetcherToTest, + GTMSessionFetcherTestResponse testResponse) { + testResponse(fakedResponse, fakedDataOrNil, fakedErrorOrNil); + }; + return service; +#else + GTMSESSION_ASSERT_DEBUG(0, @"Test blocks disabled"); + return nil; +#endif // GTM_DISABLE_FETCHER_TEST_BLOCK +} + +#pragma mark Synchronous Wait for Unit Testing + +- (BOOL)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds { + NSDate *giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + _stoppedFetchersToWaitFor = [NSMutableArray array]; + + BOOL shouldSpinRunLoop = [NSThread isMainThread]; + const NSTimeInterval kSpinInterval = 0.001; + BOOL didTimeOut = NO; + while (([self numberOfFetchers] > 0 || _stoppedFetchersToWaitFor.count > 0)) { + didTimeOut = [giveUpDate timeIntervalSinceNow] < 0; + if (didTimeOut) break; + + GTMSessionFetcher *stoppedFetcher = _stoppedFetchersToWaitFor.firstObject; + if (stoppedFetcher) { + [_stoppedFetchersToWaitFor removeObject:stoppedFetcher]; + [stoppedFetcher waitForCompletionWithTimeout:10.0 * kSpinInterval]; + } + + if (shouldSpinRunLoop) { + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:kSpinInterval]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } else { + [NSThread sleepForTimeInterval:kSpinInterval]; + } + } + _stoppedFetchersToWaitFor = nil; + + return !didTimeOut; +} + +@end + +@implementation GTMSessionFetcherService (BackwardsCompatibilityOnly) + +- (NSInteger)cookieStorageMethod { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _cookieStorageMethod; + } +} + +- (void)setCookieStorageMethod:(NSInteger)cookieStorageMethod { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _cookieStorageMethod = cookieStorageMethod; + } +} + +@end + +@implementation GTMSessionFetcherSessionDelegateDispatcher { + __weak GTMSessionFetcherService *_parentService; + NSURLSession *_session; + + // The task map maps NSURLSessionTasks to GTMSessionFetchers + NSMutableDictionary *_taskToFetcherMap; + // The discard timer will invalidate sessions after the session's last task completes. + NSTimer *_discardTimer; + NSTimeInterval _discardInterval; +} + +@synthesize discardInterval = _discardInterval, + session = _session; + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithParentService:(GTMSessionFetcherService *)parentService + sessionDiscardInterval:(NSTimeInterval)discardInterval { + self = [super init]; + if (self) { + _discardInterval = discardInterval; + _parentService = parentService; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p %@ %@", + [self class], self, + _session ?: @"<no session>", + _taskToFetcherMap.count > 0 ? _taskToFetcherMap : @"<no tasks>"]; +} + +- (NSTimer *)discardTimer { + GTMSessionCheckNotSynchronized(self); + @synchronized(self) { + return _discardTimer; + } +} + +// This method should be called inside of a @synchronized(self) block. +- (void)startDiscardTimer { + GTMSessionCheckSynchronized(self); + [_discardTimer invalidate]; + _discardTimer = nil; + if (_discardInterval > 0) { + _discardTimer = [NSTimer timerWithTimeInterval:_discardInterval + target:self + selector:@selector(discardTimerFired:) + userInfo:nil + repeats:NO]; + [_discardTimer setTolerance:(_discardInterval / 10)]; + [[NSRunLoop mainRunLoop] addTimer:_discardTimer forMode:NSRunLoopCommonModes]; + } +} + +// This method should be called inside of a @synchronized(self) block. +- (void)destroyDiscardTimer { + GTMSessionCheckSynchronized(self); + [_discardTimer invalidate]; + _discardTimer = nil; +} + +- (void)discardTimerFired:(NSTimer *)timer { + GTMSessionFetcherService *service; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + NSUInteger numberOfTasks = _taskToFetcherMap.count; + if (numberOfTasks == 0) { + service = _parentService; + } + } + + // Inform the service that the discard timer has fired, and should check whether the + // service can abandon us. -resetSession cannot be called directly, as there is a + // race condition that must be guarded against with the NSURLSession being returned + // from sessionForFetcherCreation outside other locks. The service can take steps + // to prevent resetting the session if that has occurred. + // + // The service must be called from outside the @synchronized block. + [service resetSessionForDispatcherDiscardTimer:timer]; +} + +- (void)abandon { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self destroySessionAndTimer]; + } +} + +- (void)startSessionUsage { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [self destroyDiscardTimer]; + } +} + +// This method should be called inside of a @synchronized(self) block. +- (void)destroySessionAndTimer { + GTMSessionCheckSynchronized(self); + [self destroyDiscardTimer]; + + // Break any retain cycle from the session holding the delegate. + [_session finishTasksAndInvalidate]; + + // Immediately clear the session so no new task may be issued with it. + // + // The _taskToFetcherMap needs to stay valid until the outstanding tasks finish. + _session = nil; +} + +- (void)setFetcher:(GTMSessionFetcher *)fetcher forTask:(NSURLSessionTask *)task { + GTMSESSION_ASSERT_DEBUG(fetcher != nil, @"missing fetcher"); + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_taskToFetcherMap == nil) { + _taskToFetcherMap = [[NSMutableDictionary alloc] init]; + } + + if (fetcher) { + [_taskToFetcherMap setObject:fetcher forKey:task]; + [self destroyDiscardTimer]; + } + } +} + +- (void)removeFetcher:(GTMSessionFetcher *)fetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + // Typically, a fetcher should be removed when its task invokes + // URLSession:task:didCompleteWithError:. + // + // When fetching with a testBlock, though, the task completed delegate + // method may not be invoked, requiring cleanup here. + NSArray *tasks = [_taskToFetcherMap allKeysForObject:fetcher]; + GTMSESSION_ASSERT_DEBUG(tasks.count <= 1, @"fetcher task not unmapped: %@", tasks); + [_taskToFetcherMap removeObjectsForKeys:tasks]; + + if (_taskToFetcherMap.count == 0) { + [self startDiscardTimer]; + } + } +} + +// This helper method provides synchronized access to the task map for the delegate +// methods below. +- (id)fetcherForTask:(NSURLSessionTask *)task { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return [_taskToFetcherMap objectForKey:task]; + } +} + +- (void)removeTaskFromMap:(NSURLSessionTask *)task { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + [_taskToFetcherMap removeObjectForKey:task]; + } +} + +- (void)setSession:(NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _session = session; + } +} + +- (NSURLSession *)session { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _session; + } +} + +- (NSTimeInterval)discardInterval { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _discardInterval; + } +} + +- (void)setDiscardInterval:(NSTimeInterval)interval { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _discardInterval = interval; + } +} + +// NSURLSessionDelegate protocol methods. + +// - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session; +// +// TODO(seh): How do we route this to an appropriate fetcher? + + +- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { + GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ didBecomeInvalidWithError:%@", + [self class], self, session, error); + NSDictionary *localTaskToFetcherMap; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _session = nil; + + localTaskToFetcherMap = [_taskToFetcherMap copy]; + } + + // Any "suspended" tasks may not have received callbacks from NSURLSession when the session + // completes; we'll call them now. + [localTaskToFetcherMap enumerateKeysAndObjectsUsingBlock:^(NSURLSessionTask *task, + GTMSessionFetcher *fetcher, + BOOL *stop) { + if (fetcher.session == session) { + // Our delegate method URLSession:task:didCompleteWithError: will rely on + // _taskToFetcherMap so that should still contain this fetcher. + NSError *canceledError = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + userInfo:nil]; + [self URLSession:session task:task didCompleteWithError:canceledError]; + } else { + GTMSESSION_ASSERT_DEBUG(0, @"Unexpected session in fetcher: %@ has %@ (expected %@)", + fetcher, fetcher.session, session); + } + }]; + + // Our tests rely on this notification to know the session discard timer fired. + NSDictionary *userInfo = @{ kGTMSessionFetcherServiceSessionKey : session }; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMSessionFetcherServiceSessionBecameInvalidNotification + object:_parentService + userInfo:userInfo]; +} + + +#pragma mark - NSURLSessionTaskDelegate + +// NSURLSessionTaskDelegate protocol methods. +// +// We won't test here if the fetcher responds to these since we only want this +// class to implement the same delegate methods the fetcher does (so NSURLSession's +// tests for respondsToSelector: will have the same result whether the session +// delegate is the fetcher or this dispatcher.) + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler { + id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task]; + [fetcher URLSession:session + task:task +willPerformHTTPRedirection:response + newRequest:request + completionHandler:completionHandler]; +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))handler { + id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task]; + [fetcher URLSession:session + task:task + didReceiveChallenge:challenge + completionHandler:handler]; +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + needNewBodyStream:(void (^)(NSInputStream *bodyStream))handler { + id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task]; + [fetcher URLSession:session + task:task + needNewBodyStream:handler]; +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task]; + [fetcher URLSession:session + task:task + didSendBodyData:bytesSent + totalBytesSent:totalBytesSent +totalBytesExpectedToSend:totalBytesExpectedToSend]; +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error { + id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task]; + + // This is the usual way tasks are removed from the task map. + [self removeTaskFromMap:task]; + + [fetcher URLSession:session + task:task + didCompleteWithError:error]; +} + +// NSURLSessionDataDelegate protocol methods. + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition))handler { + id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask]; + [fetcher URLSession:session + dataTask:dataTask + didReceiveResponse:response + completionHandler:handler]; +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask { + id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask]; + GTMSESSION_ASSERT_DEBUG(fetcher != nil, @"Missing fetcher for %@", dataTask); + [self removeTaskFromMap:dataTask]; + if (fetcher) { + GTMSESSION_ASSERT_DEBUG([fetcher isKindOfClass:[GTMSessionFetcher class]], + @"Expecting GTMSessionFetcher"); + [self setFetcher:(GTMSessionFetcher *)fetcher forTask:downloadTask]; + } + + [fetcher URLSession:session + dataTask:dataTask +didBecomeDownloadTask:downloadTask]; +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data { + id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask]; + [fetcher URLSession:session + dataTask:dataTask + didReceiveData:data]; +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *))handler { + id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask]; + [fetcher URLSession:session + dataTask:dataTask + willCacheResponse:proposedResponse + completionHandler:handler]; +} + +// NSURLSessionDownloadDelegate protocol methods. + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location { + id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask]; + [fetcher URLSession:session + downloadTask:downloadTask +didFinishDownloadingToURL:location]; +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalWritten +totalBytesExpectedToWrite:(int64_t)totalExpected { + id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask]; + [fetcher URLSession:session + downloadTask:downloadTask + didWriteData:bytesWritten + totalBytesWritten:totalWritten +totalBytesExpectedToWrite:totalExpected]; +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes { + id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask]; + [fetcher URLSession:session + downloadTask:downloadTask + didResumeAtOffset:fileOffset + expectedTotalBytes:expectedTotalBytes]; +} + +@end diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h b/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h @@ -0,0 +1,166 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +// GTMSessionUploadFetcher implements Google's resumable upload protocol. + +// +// This subclass of GTMSessionFetcher simulates the series of fetches +// needed for chunked upload as a single fetch operation. +// +// Protocol document: TBD +// +// To the client, the only fetcher that exists is this class; the subsidiary +// fetchers needed for uploading chunks are not visible (though the most recent +// chunk fetcher may be accessed via the -activeFetcher or -chunkFetcher methods, and +// -responseHeaders and -statusCode reflect results from the most recent chunk +// fetcher.) +// +// Chunk fetchers are discarded as soon as they have completed. +// +// The protocol also allows for a cancellation notification request to be sent to the +// server to allow discarding of the currently uploaded data and this will be sent +// automatically upon calling stopFetching if the upload has already started. +// +// Note: Unlike the fetcher superclass, the methods of GTMSessionUploadFetcher should +// only be used from the main thread until further work is done to make this subclass +// thread-safe. + +#import "GTMSessionFetcher.h" +#import "GTMSessionFetcherService.h" + +GTM_ASSUME_NONNULL_BEGIN + +// The value to use for file size parameters when the file size is not yet known. +extern int64_t const kGTMSessionUploadFetcherUnknownFileSize; + +// Unless an application knows it needs a smaller chunk size, it should use the standard +// chunk size, which sends the entire file as a single chunk to minimize upload overhead. +// Setting an explicit chunk size that comfortably fits in memory is advisable for large +// uploads. +extern int64_t const kGTMSessionUploadFetcherStandardChunkSize; + +// When uploading requires data buffer allocations (such as uploading from an NSData or +// an NSFileHandle) this is the maximum buffer size that will be created by the fetcher. +extern int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize; + +// Notification that the upload location URL was provided by the server. +extern NSString *const kGTMSessionFetcherUploadLocationObtainedNotification; + +// Block to provide data during uploads. +// +// Response data may be allocated with dataWithBytesNoCopy:length:freeWhenDone: for efficiency, +// and released after the response block returns. +// +// If the length of the file being uploaded is unknown or already set, send +// kGTMSessionUploadFetcherUnknownFileSize for |fullUploadLength|. Otherwise, set |fullUploadLength| +// to its proper value. +// +// Pass nil as the data (and optionally an NSError) for a failure. +typedef void (^GTMSessionUploadFetcherDataProviderResponse)(NSData * GTM_NULLABLE_TYPE data, + int64_t fullUploadLength, + NSError * GTM_NULLABLE_TYPE error); +// Do not call the response with an NSData object with less data than the requested length unless +// you are passing the fullUploadLength to the fetcher for the first time and it is the last chunk +// of data in the file being uploaded. +typedef void (^GTMSessionUploadFetcherDataProvider)(int64_t offset, int64_t length, + GTMSessionUploadFetcherDataProviderResponse response); + +// Block to be notified about the final status of the cancellation request started in stopFetching. +// +// |fetcher| will be the cancel request that was sent to the server, or nil if stopFetching is not +// going to send a cancel request. If |fetcher| is provided, the other parameters correspond to the +// completion handler of the cancellation request fetcher. +typedef void (^GTMSessionUploadFetcherCancellationHandler)( + GTMSessionFetcher * GTM_NULLABLE_TYPE fetcher, + NSData * GTM_NULLABLE_TYPE data, + NSError * GTM_NULLABLE_TYPE error); + +@interface GTMSessionUploadFetcher : GTMSessionFetcher + +// Create an upload fetcher specifying either the request or the resume location URL, +// then set an upload data source using one of these: +// +// setUploadFileURL: +// setUploadDataLength:provider: +// setUploadFileHandle: +// setUploadData: + ++ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil; + ++ (instancetype)uploadFetcherWithLocation:(NSURL * GTM_NULLABLE_TYPE)uploadLocationURL + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil; + +// Allows dataProviders for files of unknown length. Pass kGTMSessionUploadFetcherUnknownFileSize as +// |fullLength| if the length is unknown. +- (void)setUploadDataLength:(int64_t)fullLength + provider:(GTM_NULLABLE GTMSessionUploadFetcherDataProvider)block; + ++ (NSArray *)uploadFetchersForBackgroundSessions; ++ (GTM_NULLABLE instancetype)uploadFetcherForSessionIdentifier:(NSString *)sessionIdentifier; + +- (void)pauseFetching; +- (void)resumeFetching; +- (BOOL)isPaused; + +@property(atomic, strong, GTM_NULLABLE) NSURL *uploadLocationURL; +@property(atomic, strong, GTM_NULLABLE) NSData *uploadData; +@property(atomic, strong, GTM_NULLABLE) NSURL *uploadFileURL; +@property(atomic, strong, GTM_NULLABLE) NSFileHandle *uploadFileHandle; +@property(atomic, copy, readonly, GTM_NULLABLE) GTMSessionUploadFetcherDataProvider uploadDataProvider; +@property(atomic, copy) NSString *uploadMIMEType; +@property(atomic, assign) int64_t chunkSize; +@property(atomic, readonly, assign) int64_t currentOffset; + +// The fetcher for the current data chunk, if any +@property(atomic, strong, GTM_NULLABLE) GTMSessionFetcher *chunkFetcher; + +// The active fetcher is the current chunk fetcher, or the upload fetcher itself +// if no chunk fetcher has yet been created. +@property(atomic, readonly) GTMSessionFetcher *activeFetcher; + +// The last request made by an active fetcher. Useful for testing. +@property(atomic, readonly, GTM_NULLABLE) NSURLRequest *lastChunkRequest; + +// The status code from the most recently-completed fetch. +@property(atomic, assign) NSInteger statusCode; + +// Invoked as part of the stop fetching process. Invoked immediately if there is no upload in +// progress, otherwise invoked with the results of the attempt to notify the server that the +// upload will not continue. +// +// Unlike other callbacks, since this is related specifically to the stopFetching flow it is not +// cleared by stopFetching. It will instead clear itself after it is invoked or if the completion +// has occured before stopFetching is called. +@property(atomic, copy, GTM_NULLABLE) GTMSessionUploadFetcherCancellationHandler + cancellationHandler; + +// Exposed for testing only. +@property(atomic, readonly, GTM_NULLABLE) dispatch_queue_t delegateCallbackQueue; +@property(atomic, readonly, GTM_NULLABLE) GTMSessionFetcherCompletionHandler delegateCompletionHandler; + +@end + +@interface GTMSessionFetcher (GTMSessionUploadFetcherMethods) + +@property(readonly, GTM_NULLABLE) GTMSessionUploadFetcher *parentUploadFetcher; + +@end + +GTM_ASSUME_NONNULL_END diff --git a/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m b/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m @@ -0,0 +1,1954 @@ +/* Copyright 2014 Google Inc. All rights reserved. + * + * 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GTMSessionUploadFetcher.h" + +static NSString *const kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey = @"_upChunk"; +static NSString *const kGTMSessionIdentifierUploadFileURLMetadataKey = @"_upFileURL"; +static NSString *const kGTMSessionIdentifierUploadFileLengthMetadataKey = @"_upFileLen"; +static NSString *const kGTMSessionIdentifierUploadLocationURLMetadataKey = @"_upLocURL"; +static NSString *const kGTMSessionIdentifierUploadMIMETypeMetadataKey = @"_uploadMIME"; +static NSString *const kGTMSessionIdentifierUploadChunkSizeMetadataKey = @"_upChSize"; +static NSString *const kGTMSessionIdentifierUploadCurrentOffsetMetadataKey = @"_upOffset"; + +static NSString *const kGTMSessionHeaderXGoogUploadChunkGranularity = @"X-Goog-Upload-Chunk-Granularity"; +static NSString *const kGTMSessionHeaderXGoogUploadCommand = @"X-Goog-Upload-Command"; +static NSString *const kGTMSessionHeaderXGoogUploadContentLength = @"X-Goog-Upload-Content-Length"; +static NSString *const kGTMSessionHeaderXGoogUploadContentType = @"X-Goog-Upload-Content-Type"; +static NSString *const kGTMSessionHeaderXGoogUploadOffset = @"X-Goog-Upload-Offset"; +static NSString *const kGTMSessionHeaderXGoogUploadProtocol = @"X-Goog-Upload-Protocol"; +static NSString *const kGTMSessionXGoogUploadProtocolResumable = @"resumable"; +static NSString *const kGTMSessionHeaderXGoogUploadSizeReceived = @"X-Goog-Upload-Size-Received"; +static NSString *const kGTMSessionHeaderXGoogUploadStatus = @"X-Goog-Upload-Status"; +static NSString *const kGTMSessionHeaderXGoogUploadURL = @"X-Goog-Upload-URL"; + +// Property of chunk fetchers identifying the parent upload fetcher. Non-retained NSValue. +static NSString *const kGTMSessionUploadFetcherChunkParentKey = @"_uploadFetcherChunkParent"; + +int64_t const kGTMSessionUploadFetcherUnknownFileSize = -1; + +int64_t const kGTMSessionUploadFetcherStandardChunkSize = (int64_t)LLONG_MAX; + +#if TARGET_OS_IPHONE +int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize = 10 * 1024 * 1024; // 10 MB for iOS, watchOS, tvOS +#else +int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize = 100 * 1024 * 1024; // 100 MB for macOS +#endif + +typedef NS_ENUM(NSUInteger, GTMSessionUploadFetcherStatus) { + kStatusUnknown, + kStatusActive, + kStatusFinal, + kStatusCancelled, +}; + +NSString *const kGTMSessionFetcherUploadLocationObtainedNotification = + @"kGTMSessionFetcherUploadLocationObtainedNotification"; + +#if !GTMSESSION_BUILD_COMBINED_SOURCES +@interface GTMSessionFetcher (ProtectedMethods) + +// Access to non-public method on the parent fetcher class. +- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks; +- (void)createSessionIdentifierWithMetadata:(NSDictionary *)metadata; +- (GTMSessionFetcherCompletionHandler)completionHandlerWithTarget:(id)target + didFinishSelector:(SEL)finishedSelector; +- (void)invokeOnCallbackQueue:(dispatch_queue_t)callbackQueue + afterUserStopped:(BOOL)afterStopped + block:(void (^)(void))block; +- (NSTimer *)retryTimer; +- (void)beginFetchForRetry; + +@property(readwrite, strong) NSData *downloadedData; +- (void)releaseCallbacks; + +- (NSInteger)statusCodeUnsynchronized; + +- (BOOL)userStoppedFetching; + +@end +#endif // !GTMSESSION_BUILD_COMBINED_SOURCES + +@interface GTMSessionUploadFetcher () + +// Changing readonly to readwrite. +@property(atomic, strong, readwrite) NSURLRequest *lastChunkRequest; +@property(atomic, readwrite, assign) int64_t currentOffset; + +// Internal properties. +@property(strong, atomic, GTM_NULLABLE) GTMSessionFetcher *fetcherInFlight; // Synchronized on self. + +@property(assign, atomic, getter=isSubdataGenerating) BOOL subdataGenerating; +@property(assign, atomic) BOOL shouldInitiateOffsetQuery; +@property(assign, atomic) int64_t uploadGranularity; + +@end + +@implementation GTMSessionUploadFetcher { + GTMSessionFetcher *_chunkFetcher; + + // We'll call through to the delegate's completion handler. + GTMSessionFetcherCompletionHandler _delegateCompletionHandler; + dispatch_queue_t _delegateCallbackQueue; + + // The initial fetch's body length and bytes actually sent are + // needed for calculating progress during subsequent chunk uploads + int64_t _initialBodyLength; + int64_t _initialBodySent; + + // The upload server address for the chunks of this upload session. + NSURL *_uploadLocationURL; + + // _uploadData, _uploadDataProvider, or _uploadFileHandle may be set, but only one. + NSData *_uploadData; + NSFileHandle *_uploadFileHandle; + GTMSessionUploadFetcherDataProvider _uploadDataProvider; + NSURL *_uploadFileURL; + int64_t _uploadFileLength; + NSString *_uploadMIMEType; + int64_t _chunkSize; + int64_t _uploadGranularity; + BOOL _isPaused; + BOOL _isRestartedUpload; + BOOL _shouldInitiateOffsetQuery; + + // Tied to useBackgroundSession property, since this property is applicable to chunk fetchers. + BOOL _useBackgroundSessionOnChunkFetchers; + + // We keep the latest offset into the upload data just for progress reporting. + int64_t _currentOffset; + + NSDictionary *_recentChunkReponseHeaders; + NSInteger _recentChunkStatusCode; + + // For waiting, we need to know the fetcher in flight, if any, and if subdata generation + // is in progress. + GTMSessionFetcher *_fetcherInFlight; + BOOL _isSubdataGenerating; + BOOL _isCancelInFlight; + + GTMSessionUploadFetcherCancellationHandler _cancellationHandler; +} + ++ (void)load { + [self uploadFetchersForBackgroundSessions]; +} + ++ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTMSessionFetcherService *)fetcherService { + GTMSessionUploadFetcher *fetcher = [self uploadFetcherWithRequest:request + fetcherService:fetcherService]; + [fetcher setLocationURL:nil + uploadMIMEType:uploadMIMEType + chunkSize:chunkSize]; + return fetcher; +} + ++ (instancetype)uploadFetcherWithLocation:(NSURL * GTM_NULLABLE_TYPE)uploadLocationURL + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTMSessionFetcherService *)fetcherService { + GTMSessionUploadFetcher *fetcher = [self uploadFetcherWithRequest:nil + fetcherService:fetcherService]; + [fetcher setLocationURL:uploadLocationURL + uploadMIMEType:uploadMIMEType + chunkSize:chunkSize]; + return fetcher; +} + ++ (instancetype)uploadFetcherForSessionIdentifierMetadata:(NSDictionary *)metadata { + GTMSESSION_ASSERT_DEBUG( + [metadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] boolValue], + @"Session identifier metadata is not for an upload fetcher: %@", metadata); + + NSNumber *uploadFileLengthNum = metadata[kGTMSessionIdentifierUploadFileLengthMetadataKey]; + GTMSESSION_ASSERT_DEBUG(uploadFileLengthNum != nil, + @"Session metadata missing an UploadFileSize"); + if (uploadFileLengthNum == nil) return nil; + + int64_t uploadFileLength = [uploadFileLengthNum longLongValue]; + GTMSESSION_ASSERT_DEBUG(uploadFileLength >= 0, @"Session metadata UploadFileSize is unknown"); + + NSString *uploadFileURLString = metadata[kGTMSessionIdentifierUploadFileURLMetadataKey]; + GTMSESSION_ASSERT_DEBUG(uploadFileURLString, @"Session metadata missing an UploadFileURL"); + if (uploadFileURLString == nil) return nil; + + NSURL *uploadFileURL = [NSURL URLWithString:uploadFileURLString]; + // There used to be a call here to NSURL checkResourceIsReachableAndReturnError: to check for the + // existence of the file (also tried NSFileManager fileExistsAtPath:). We've determined + // empirically that the check can fail at startup even when the upload file does in fact exist. + // For now, we'll go ahead and restore the background upload fetcher. If the file doesn't exist, + // it will fail later. + + NSString *uploadLocationURLString = metadata[kGTMSessionIdentifierUploadLocationURLMetadataKey]; + NSURL *uploadLocationURL = + uploadLocationURLString ? [NSURL URLWithString:uploadLocationURLString] : nil; + + NSString *uploadMIMEType = + metadata[kGTMSessionIdentifierUploadMIMETypeMetadataKey]; + int64_t uploadChunkSize = + [metadata[kGTMSessionIdentifierUploadChunkSizeMetadataKey] longLongValue]; + if (uploadChunkSize <= 0) { + uploadChunkSize = kGTMSessionUploadFetcherStandardChunkSize; + } + int64_t currentOffset = + [metadata[kGTMSessionIdentifierUploadCurrentOffsetMetadataKey] longLongValue]; + GTMSESSION_ASSERT_DEBUG(currentOffset <= uploadFileLength, + @"CurrentOffset (%lld) exceeds UploadFileSize (%lld)", + currentOffset, uploadFileLength); + if (currentOffset > uploadFileLength) return nil; + + GTMSessionUploadFetcher *uploadFetcher = [self uploadFetcherWithLocation:uploadLocationURL + uploadMIMEType:uploadMIMEType + chunkSize:uploadChunkSize + fetcherService:nil]; + // Set the upload file length before setting the upload file URL tries to determine the length. + [uploadFetcher setUploadFileLength:uploadFileLength]; + + uploadFetcher.uploadFileURL = uploadFileURL; + uploadFetcher.sessionUserInfo = metadata; + uploadFetcher.useBackgroundSession = YES; + uploadFetcher.currentOffset = currentOffset; + uploadFetcher.delegateCallbackQueue = uploadFetcher.callbackQueue; + uploadFetcher.allowedInsecureSchemes = @[ @"http" ]; // Allowed on restored upload fetcher. + return uploadFetcher; +} + ++ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request + fetcherService:(GTMSessionFetcherService *)fetcherService { + // Internal utility method for instantiating fetchers + GTMSessionUploadFetcher *fetcher; + if ([fetcherService isKindOfClass:[GTMSessionFetcherService class]]) { + fetcher = [fetcherService fetcherWithRequest:request + fetcherClass:self]; + } else { + fetcher = [self fetcherWithRequest:request]; + } + fetcher.useBackgroundSession = YES; + return fetcher; +} + ++ (NSPointerArray *)uploadFetcherPointerArrayForBackgroundSessions { + static NSPointerArray *gUploadFetcherPointerArrayForBackgroundSessions = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gUploadFetcherPointerArrayForBackgroundSessions = [NSPointerArray weakObjectsPointerArray]; + }); + return gUploadFetcherPointerArrayForBackgroundSessions; +} + ++ (instancetype)uploadFetcherForSessionIdentifier:(NSString *)sessionIdentifier { + GTMSESSION_ASSERT_DEBUG(sessionIdentifier != nil, @"Invalid session identifier"); + NSArray *uploadFetchersForBackgroundSessions = [self uploadFetchersForBackgroundSessions]; + for (GTMSessionUploadFetcher *uploadFetcher in uploadFetchersForBackgroundSessions) { + if ([uploadFetcher.chunkFetcher.sessionIdentifier isEqual:sessionIdentifier]) { + return uploadFetcher; + } + } + return nil; +} + ++ (NSArray *)uploadFetchersForBackgroundSessions { + // Collect the background session upload fetchers that are still in memory. + NSPointerArray *uploadFetcherPointerArray = [self uploadFetcherPointerArrayForBackgroundSessions]; + [uploadFetcherPointerArray compact]; + NSMutableSet *restoredSessionIdentifiers = [[NSMutableSet alloc] init]; + NSMutableArray *uploadFetchers = [[NSMutableArray alloc] init]; + for (GTMSessionUploadFetcher *uploadFetcher in uploadFetcherPointerArray) { + NSString *sessionIdentifier = uploadFetcher.chunkFetcher.sessionIdentifier; + if (sessionIdentifier) { + [restoredSessionIdentifiers addObject:sessionIdentifier]; + [uploadFetchers addObject:uploadFetcher]; + } + } + + // The system may have other ongoing background upload sessions. Restore upload fetchers for those + // too. + NSArray *fetchers = [GTMSessionFetcher fetchersForBackgroundSessions]; + for (GTMSessionFetcher *fetcher in fetchers) { + NSString *sessionIdentifier = fetcher.sessionIdentifier; + if (!sessionIdentifier || [restoredSessionIdentifiers containsObject:sessionIdentifier]) { + continue; + } + NSDictionary *sessionIdentifierMetadata = [fetcher sessionIdentifierMetadata]; + if (sessionIdentifierMetadata == nil) { + continue; + } + if (![sessionIdentifierMetadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] boolValue]) { + continue; + } + GTMSessionUploadFetcher *uploadFetcher = + [self uploadFetcherForSessionIdentifierMetadata:sessionIdentifierMetadata]; + if (uploadFetcher == nil) { + // Something went wrong with this upload fetcher, so kill the restored chunk fetcher. + [fetcher stopFetching]; + continue; + } + [uploadFetchers addObject:uploadFetcher]; + uploadFetcher->_chunkFetcher = fetcher; + uploadFetcher->_fetcherInFlight = fetcher; + [uploadFetcher attachSendProgressBlockToChunkFetcher:fetcher]; + fetcher.completionHandler = + [fetcher completionHandlerWithTarget:uploadFetcher + didFinishSelector:@selector(chunkFetcher:finishedWithData:error:)]; + + GTMSESSION_LOG_DEBUG(@"%@ restoring upload fetcher %@ for chunk fetcher %@", + [self class], uploadFetcher, fetcher); + } + return uploadFetchers; +} + +- (void)setUploadData:(NSData *)data { + BOOL changed = NO; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_uploadData != data) { + _uploadData = data; + changed = YES; + } + } + if (changed) { + [self setupRequestHeaders]; + } +} + +- (NSData *)uploadData { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadData; + } +} + +- (void)setUploadFileHandle:(NSFileHandle *)fh { + BOOL changed = NO; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_uploadFileHandle != fh) { + _uploadFileHandle = fh; + changed = YES; + } + } + if (changed) { + [self setupRequestHeaders]; + } +} + +- (NSFileHandle *)uploadFileHandle { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadFileHandle; + } +} + +- (void)setUploadFileURL:(NSURL *)uploadURL { + BOOL changed = NO; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_uploadFileURL != uploadURL) { + _uploadFileURL = uploadURL; + changed = YES; + } + } + if (changed) { + [self setupRequestHeaders]; + } +} + +- (NSURL *)uploadFileURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadFileURL; + } +} + +- (void)setUploadFileLength:(int64_t)fullLength { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_uploadFileLength == kGTMSessionUploadFetcherUnknownFileSize && + fullLength != kGTMSessionUploadFetcherUnknownFileSize) { + _uploadFileLength = fullLength; + } + } +} + +- (void)setUploadDataLength:(int64_t)fullLength + provider:(GTMSessionUploadFetcherDataProvider)block { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _uploadDataProvider = [block copy]; + _uploadFileLength = fullLength; + } + [self setupRequestHeaders]; +} + +- (GTMSessionUploadFetcherDataProvider)uploadDataProvider { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadDataProvider; + } +} + + +- (void)setUploadMIMEType:(NSString *)uploadMIMEType { + GTMSESSION_ASSERT_DEBUG(0, @"TODO: disallow setUploadMIMEType by making declaration readonly"); + // (and uploadMIMEType, chunksize, currentOffset) + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _uploadMIMEType = uploadMIMEType; + } +} + +- (NSString *)uploadMIMEType { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadMIMEType; + } +} + +- (void)setChunkSize:(int64_t)chunkSize { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _chunkSize = chunkSize; + } +} + +- (int64_t)chunkSize { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _chunkSize; + } +} + +- (void)setupRequestHeaders { + GTMSessionCheckNotSynchronized(self); + +#if DEBUG + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + int hasData = (_uploadData != nil) ? 1 : 0; + int hasFileHandle = (_uploadFileHandle != nil) ? 1 : 0; + int hasFileURL = (_uploadFileURL != nil) ? 1 : 0; + int hasUploadDataProvider = (_uploadDataProvider != nil) ? 1 : 0; + int numberOfSources = hasData + hasFileHandle + hasFileURL + hasUploadDataProvider; + #pragma unused(numberOfSources) + GTMSESSION_ASSERT_DEBUG(numberOfSources == 1, + @"Need just one upload source (%d)", numberOfSources); + } // @synchronized(self) +#endif + + // Add our custom headers to the initial request indicating the data + // type and total size to be delivered later in the chunk requests. + NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; + + GTMSESSION_ASSERT_DEBUG((mutableRequest == nil) != (_uploadLocationURL == nil), + @"Request and location are mutually exclusive"); + if (!mutableRequest) return; + + [mutableRequest setValue:kGTMSessionXGoogUploadProtocolResumable + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadProtocol]; + [mutableRequest setValue:@"start" + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand]; + [mutableRequest setValue:_uploadMIMEType + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadContentType]; + [mutableRequest setValue:@([self fullUploadLength]).stringValue + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadContentLength]; + + NSString *method = mutableRequest.HTTPMethod; + if (method == nil || [method caseInsensitiveCompare:@"GET"] == NSOrderedSame) { + [mutableRequest setHTTPMethod:@"POST"]; + } + + // Ensure the user agent header identifies this to the upload server as a + // GTMSessionUploadFetcher client. The /1 can be incremented in the unlikely circumstance + // we need to make a bug fix in the client that the server can recognize. + NSString *const kUserAgentStub = @"(GTMSUF/1)"; + NSString *userAgent = [mutableRequest valueForHTTPHeaderField:@"User-Agent"]; + if (userAgent == nil + || [userAgent rangeOfString:kUserAgentStub].location == NSNotFound) { + if (userAgent.length == 0) { + userAgent = GTMFetcherStandardUserAgentString(nil); + } + userAgent = [userAgent stringByAppendingFormat:@" %@", kUserAgentStub]; + [mutableRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + [self setRequest:mutableRequest]; +} + +- (void)setLocationURL:(NSURL * GTM_NULLABLE_TYPE)location + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + GTMSESSION_ASSERT_DEBUG(chunkSize > 0, @"chunk size is zero"); + + // When resuming an upload, set the known upload target URL. + _uploadLocationURL = location; + + _uploadMIMEType = uploadMIMEType; + _chunkSize = chunkSize; + + // Indicate that we've not yet determined the file handle's length + _uploadFileLength = kGTMSessionUploadFetcherUnknownFileSize; + + // Indicate that we've not yet determined the upload fetcher status + _recentChunkStatusCode = -1; + + // If this is restarting an upload begun by another fetcher, + // the location is specified but the request is nil + _isRestartedUpload = (location != nil); + } // @synchronized(self) +} + +- (int64_t)fullUploadLength { + int64_t result; + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_uploadData) { + result = (int64_t)_uploadData.length; + } else { + if (_uploadFileLength == kGTMSessionUploadFetcherUnknownFileSize) { + if (_uploadFileHandle) { + // First time through, seek to end to determine file length + _uploadFileLength = (int64_t)[_uploadFileHandle seekToEndOfFile]; + } else if (_uploadDataProvider) { + // _uploadFileLength is set when the _uploadDataProvider is set. + GTMSESSION_ASSERT_DEBUG(_uploadFileLength >= 0, @"No uploadDataProvider length set"); + } else { + NSNumber *filesizeNum; + NSError *valueError; + if ([_uploadFileURL getResourceValue:&filesizeNum + forKey:NSURLFileSizeKey + error:&valueError]) { + _uploadFileLength = filesizeNum.longLongValue; + } else { + GTMSESSION_ASSERT_DEBUG(NO, @"Cannot get file size: %@\n %@", + valueError, _uploadFileURL.path); + _uploadFileLength = 0; + } + } + } + result = _uploadFileLength; + } + } // @synchronized(self) + return result; +} + +// Make a subdata of the upload data. +- (void)generateChunkSubdataWithOffset:(int64_t)offset + length:(int64_t)length + response:(GTMSessionUploadFetcherDataProviderResponse)response { + GTMSessionUploadFetcherDataProvider uploadDataProvider = self.uploadDataProvider; + if (uploadDataProvider) { + uploadDataProvider(offset, length, response); + return; + } + + NSData *uploadData = self.uploadData; + if (uploadData) { + // NSData provided. + NSData *resultData; + if (offset == 0 && length == (int64_t)uploadData.length) { + resultData = uploadData; + } else { + int64_t dataLength = (int64_t)uploadData.length; + // Ensure our range is valid. b/18007814 + if (offset + length > dataLength) { + NSString *errorMessage = [NSString stringWithFormat: + @"Range invalid for upload data. offset: %lld\tlength: %lld\tdataLength: %lld", + offset, length, dataLength]; + GTMSESSION_ASSERT_DEBUG(NO, @"%@", errorMessage); + response(nil, + kGTMSessionUploadFetcherUnknownFileSize, + [self uploadChunkUnavailableErrorWithDescription:errorMessage]); + return; + } + NSRange range = NSMakeRange((NSUInteger)offset, (NSUInteger)length); + + @try { + resultData = [uploadData subdataWithRange:range]; + } + @catch (NSException *exception) { + NSString *errorMessage = exception.description; + GTMSESSION_ASSERT_DEBUG(NO, @"%@", errorMessage); + response(nil, + kGTMSessionUploadFetcherUnknownFileSize, + [self uploadChunkUnavailableErrorWithDescription:errorMessage]); + return; + } + } + response(resultData, kGTMSessionUploadFetcherUnknownFileSize, nil); + return; + } + NSURL *uploadFileURL = self.uploadFileURL; + if (uploadFileURL) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self generateChunkSubdataFromFileURL:uploadFileURL + offset:offset + length:length + response:response]; + }); + return; + } + GTMSESSION_ASSERT_DEBUG(_uploadFileHandle, @"Unexpectedly missing upload data package"); + NSFileHandle *uploadFileHandle = self.uploadFileHandle; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self generateChunkSubdataFromFileHandle:uploadFileHandle + offset:offset + length:length + response:response]; + }); +} + +- (void)generateChunkSubdataFromFileHandle:(NSFileHandle *)fileHandle + offset:(int64_t)offset + length:(int64_t)length + response:(GTMSessionUploadFetcherDataProviderResponse)response { + NSData *resultData; + NSError *error; + @try { + [fileHandle seekToFileOffset:(unsigned long long)offset]; + resultData = [fileHandle readDataOfLength:(NSUInteger)length]; + } + @catch (NSException *exception) { + GTMSESSION_ASSERT_DEBUG(NO, @"uploadFileHandle failed to read, %@", exception); + error = [self uploadChunkUnavailableErrorWithDescription:exception.description]; + } + // The response always re-dispatches to the main thread, so we skip doing that here. + response(resultData, kGTMSessionUploadFetcherUnknownFileSize, error); +} + +- (void)generateChunkSubdataFromFileURL:(NSURL *)fileURL + offset:(int64_t)offset + length:(int64_t)length + response:(GTMSessionUploadFetcherDataProviderResponse)response { + GTMSessionCheckNotSynchronized(self); + + NSData *resultData; + NSError *error; + int64_t fullUploadLength = [self fullUploadLength]; + NSData *mappedData = + [NSData dataWithContentsOfURL:fileURL + options:NSDataReadingMappedAlways + NSDataReadingUncached + error:&error]; + if (!mappedData) { + // We could not create an NSData by memory-mapping the file. +#if TARGET_IPHONE_SIMULATOR + // NSTemporaryDirectory() can differ in the simulator between app restarts, + // yet the contents for the new path remains unchanged, so try the latest temp path. + if ([error.domain isEqual:NSCocoaErrorDomain] && (error.code == NSFileReadNoSuchFileError)) { + NSString *filename = [fileURL lastPathComponent]; + NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename]; + NSURL *newFileURL = [NSURL fileURLWithPath:filePath]; + if (![newFileURL isEqual:fileURL]) { + [self generateChunkSubdataFromFileURL:newFileURL + offset:offset + length:length + response:response]; + return; + } + } +#endif + + // If the file is just too large to create an NSData for, or if for some other reason we can't + // map it, create an NSFileHandle instead to read a subset into an NSData. +#if DEBUG + NSNumber *fileSizeNum; + BOOL hasFileSize = [fileURL getResourceValue:&fileSizeNum forKey:NSURLFileSizeKey error:NULL]; + GTMSESSION_LOG_DEBUG(@"Note: uploadFileURL is falling back to creating upload chunks by reading" + @" an NSFileHandle since uploadFileURL failed to map the upload file," + @" file size %@, %@", + hasFileSize ? fileSizeNum : @"unknown", error); +#endif + + NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:fileURL + error:&error]; + if (fileHandle != nil) { + [self generateChunkSubdataFromFileHandle:fileHandle + offset:offset + length:length + response:response]; + return; + } + GTMSESSION_ASSERT_DEBUG(NO, @"uploadFileURL failed to read, %@", error); + // Fall through with the error. + } else { + // Successfully created an NSData by memory-mapping the file. + if ((NSUInteger)(offset + length) > mappedData.length) { + NSString *errorMessage = [NSString stringWithFormat: + @"Range invalid for upload data. offset: %lld\tlength: %lld\tdataLength: %lld\texpected UploadLength: %lld", + offset, length, (long long)mappedData.length, fullUploadLength]; + GTMSESSION_ASSERT_DEBUG(NO, @"%@", errorMessage); + response(nil, + kGTMSessionUploadFetcherUnknownFileSize, + [self uploadChunkUnavailableErrorWithDescription:errorMessage]); + return; + } + if (offset > 0 || length < fullUploadLength) { + NSRange range = NSMakeRange((NSUInteger)offset, (NSUInteger)length); + resultData = [mappedData subdataWithRange:range]; + } else { + resultData = mappedData; + } + } + // The response always re-dispatches to the main thread, so we skip re-dispatching here. + response(resultData, kGTMSessionUploadFetcherUnknownFileSize, error); +} + +- (NSError *)uploadChunkUnavailableErrorWithDescription:(NSString *)description { + // The description in the userInfo is intended as a clue to programmers, not + // for client code to examine or rely on. + NSDictionary *userInfo = @{ @"description" : description }; + return [NSError errorWithDomain:kGTMSessionFetcherErrorDomain + code:GTMSessionFetcherErrorUploadChunkUnavailable + userInfo:userInfo]; +} + +- (NSError *)prematureFailureErrorWithUserInfo:(NSDictionary *)userInfo { + // An error for if we get an unexpected status from the upload server or + // otherwise cannot continue. This is an issue beyond the upload protocol; + // there's no way the client can do anything useful except give up. + NSError *error = [NSError errorWithDomain:kGTMSessionFetcherStatusDomain + code:501 // Not implemented + userInfo:userInfo]; + return error; +} + ++ (GTMSessionUploadFetcherStatus)uploadStatusFromResponseHeaders:(NSDictionary *)responseHeaders { + NSString *statusString = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus]; + if ([statusString isEqual:@"active"]) { + return kStatusActive; + } + if ([statusString isEqual:@"final"]) { + return kStatusFinal; + } + if ([statusString isEqual:@"cancelled"]) { + return kStatusCancelled; + } + return kStatusUnknown; +} + +#pragma mark Method overrides affecting the initial fetch only + +- (void)setCompletionHandler:(GTMSessionFetcherCompletionHandler)handler { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _delegateCompletionHandler = handler; + } +} + +- (void)setDelegateCallbackQueue:(dispatch_queue_t GTM_NULLABLE_TYPE)queue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _delegateCallbackQueue = queue; + } +} + +- (dispatch_queue_t GTM_NULLABLE_TYPE)delegateCallbackQueue { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _delegateCallbackQueue; + } +} + +- (BOOL)isRestartedUpload { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _isRestartedUpload; + } +} + +- (GTMSessionFetcher * GTM_NULLABLE_TYPE)chunkFetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _chunkFetcher; + } +} + +- (void)setChunkFetcher:(GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _chunkFetcher = fetcher; + } +} + +- (void)setFetcherInFlight:(GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _fetcherInFlight = fetcher; + } +} + +- (GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcherInFlight { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _fetcherInFlight; + } +} + +- (void)setCancellationHandler:(GTMSessionUploadFetcherCancellationHandler GTM_NULLABLE_TYPE) + cancellationHandler { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _cancellationHandler = cancellationHandler; + } +} + +- (GTMSessionUploadFetcherCancellationHandler GTM_NULLABLE_TYPE)cancellationHandler { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _cancellationHandler; + } +} + +- (void)beginFetchForRetry { + GTMSessionCheckNotSynchronized(self); + + // Override the superclass to reset the initial body length and fetcher-in-flight, + // then call the superclass implementation. + [self setInitialBodyLength:[self bodyLength]]; + + GTMSESSION_ASSERT_DEBUG(self.fetcherInFlight == nil, @"unexpected fetcher in flight: %@", + self.fetcherInFlight); + self.fetcherInFlight = self; + [super beginFetchForRetry]; +} + +- (void)beginFetchWithCompletionHandler:(GTMSessionFetcherCompletionHandler)handler { + GTMSessionCheckNotSynchronized(self); + + [self setInitialBodyLength:[self bodyLength]]; + + // We'll hold onto the superclass's callback queue so we can invoke the handler + // even after the superclass has released the queue and its callback handler, as + // happens during auth failure. + [self setDelegateCallbackQueue:self.callbackQueue]; + self.completionHandler = handler; + + if ([self isRestartedUpload]) { + // When restarting an upload, we know the destination location for chunk fetches, + // but we need to query to find the initial offset. + if (![self isPaused]) { + [self sendQueryForUploadOffsetWithFetcherProperties:self.properties]; + } + return; + } + // We don't want to call into the client's completion block immediately + // after the finish of the initial connection (the delegate is called only + // when uploading finishes), so we substitute our own completion block to be + // called when the initial connection finishes + GTMSESSION_ASSERT_DEBUG(self.fetcherInFlight == nil, @"unexpected fetcher in flight: %@", + self.fetcherInFlight); + + self.fetcherInFlight = self; + [super beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { + self.fetcherInFlight = nil; + // callback + + BOOL hasTestBlock = (self.testBlock != nil); + if (![self isRestartedUpload] && !hasTestBlock) { + if (error == nil) { + [self beginChunkFetches]; + } else { + if ([self retryTimer] == nil) { + [self invokeFinalCallbackWithData:nil + error:error + shouldInvalidateLocation:YES]; + } + } + } else { + // If there was no initial request, then this fetch is resuming some + // other uploadFetcher's initial request, and the superclass's connection + // is never used, so at this point we call the user's actual completion + // block. + if (!hasTestBlock) { + [self invokeFinalCallbackWithData:data + error:error + shouldInvalidateLocation:YES]; + } else { + // There was a test block, so we won't do chunk fetches, but we simulate obtaining + // the data to be uploaded from the upload data provider block or the file handle, + // and then call back. + [self generateChunkSubdataWithOffset:0 + length:[self fullUploadLength] + response:^(NSData *generateData, int64_t fullUploadLength, NSError *generateError) { + [self invokeFinalCallbackWithData:data + error:error + shouldInvalidateLocation:YES]; + }]; + } + } + }]; +} + +- (void)beginChunkFetches { + GTMSessionCheckNotSynchronized(self); + +#if DEBUG + // The initial response of the resumable upload protocol should have an + // empty body + // + // This assert typically happens because the upload create/edit link URL was + // not supplied with the request, and the server is thus expecting a non- + // resumable request/response. + if (self.downloadedData.length > 0) { + NSData *downloadedData = self.downloadedData; + NSString *str = [[NSString alloc] initWithData:downloadedData + encoding:NSUTF8StringEncoding]; + #pragma unused(str) + GTMSESSION_ASSERT_DEBUG(NO, @"unexpected response data (uploading to the wrong URL?)\n%@", str); + } +#endif + + // We need to get the upload URL from the location header to continue. + NSDictionary *responseHeaders = [self responseHeaders]; + + [self retrieveUploadChunkGranularityFromResponseHeaders:responseHeaders]; + + GTMSessionUploadFetcherStatus uploadStatus = + [[self class] uploadStatusFromResponseHeaders:responseHeaders]; + GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown, + @"beginChunkFetches has unexpected upload status for headers %@", responseHeaders); + + BOOL isPrematureStop = (uploadStatus == kStatusFinal) || (uploadStatus == kStatusCancelled); + + NSString *uploadLocationURLStr = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadURL]; + BOOL hasUploadLocation = (uploadLocationURLStr.length > 0); + + if (isPrematureStop || !hasUploadLocation) { + GTMSESSION_ASSERT_DEBUG(NO, @"Premature failure: upload-status:\"%@\" location:%@", + [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus], uploadLocationURLStr); + // We cannot continue since we do not know the location to use + // as our upload destination. + NSDictionary *userInfo = nil; + NSData *downloadedData = self.downloadedData; + if (downloadedData.length > 0) { + userInfo = @{ kGTMSessionFetcherStatusDataKey : downloadedData }; + } + NSError *failureError = [self prematureFailureErrorWithUserInfo:userInfo]; + [self invokeFinalCallbackWithData:nil + error:failureError + shouldInvalidateLocation:YES]; + return; + } + + self.uploadLocationURL = [NSURL URLWithString:uploadLocationURLStr]; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMSessionFetcherUploadLocationObtainedNotification + object:self]; + + // we've now sent all of the initial post body data, so we need to include + // its size in future progress indicator callbacks + [self setInitialBodySent:[self initialBodyLength]]; + + // just in case the user paused us during the initial fetch... + if (![self isPaused]) { + [self uploadNextChunkWithOffset:0]; + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent + totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + // Overrides the superclass. + [self invokeDelegateWithDidSendBytes:bytesSent + totalBytesSent:totalBytesSent + totalBytesExpectedToSend:totalBytesExpectedToSend + [self fullUploadLength]]; +} + +- (BOOL)shouldReleaseCallbacksUponCompletion { + // Overrides the superclass. + + // We don't want the superclass to release the delegate and callback + // blocks once the initial fetch has finished + // + // This is invoked for only successful completion of the connection; + // an error always will invoke and release the callbacks + return NO; +} + +- (void)invokeFinalCallbackWithData:(NSData *)data + error:(NSError *)error + shouldInvalidateLocation:(BOOL)shouldInvalidateLocation { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (shouldInvalidateLocation) { + _uploadLocationURL = nil; + } + + dispatch_queue_t queue = _delegateCallbackQueue; + GTMSessionFetcherCompletionHandler handler = _delegateCompletionHandler; + if (queue && handler) { + [self invokeOnCallbackQueue:queue + afterUserStopped:NO + block:^{ + handler(data, error); + }]; + } + } // @synchronized(self) + + [self releaseUploadAndBaseCallbacks:!self.userStoppedFetching]; +} + +- (void)releaseUploadAndBaseCallbacks:(BOOL)shouldReleaseCancellation { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _delegateCallbackQueue = nil; + _delegateCompletionHandler = nil; + _uploadDataProvider = nil; + if (shouldReleaseCancellation) { + _cancellationHandler = nil; + } + } + + // Release the base class's callbacks, too, if needed. + [self releaseCallbacks]; +} + +- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks { + GTMSessionCheckNotSynchronized(self); + + // Clear _fetcherInFlight when stopped. Moved from stopFetching, since that's a public method, + // where this method does the work. Fixes issue clearing value when retryBlock included. + GTMSessionFetcher *fetcherInFlight = self.fetcherInFlight; + if (fetcherInFlight == self) { + self.fetcherInFlight = nil; + } + + [super stopFetchReleasingCallbacks:shouldReleaseCallbacks]; + + if (shouldReleaseCallbacks) { + [self releaseUploadAndBaseCallbacks:NO]; + } +} + +#pragma mark Chunk fetching methods + +- (void)uploadNextChunkWithOffset:(int64_t)offset { + // use the properties in each chunk fetcher + NSDictionary *props = [self properties]; + + [self uploadNextChunkWithOffset:offset + fetcherProperties:props]; +} + +- (void)sendQueryForUploadOffsetWithFetcherProperties:(NSDictionary *)props { + GTMSessionFetcher *queryFetcher = [self uploadFetcherWithProperties:props + isQueryFetch:YES]; + queryFetcher.bodyData = [NSData data]; + + NSString *originalComment = self.comment; + [queryFetcher setCommentWithFormat:@"%@ (query offset)", + originalComment ? originalComment : @"upload"]; + + [queryFetcher setRequestValue:@"query" forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand]; + + self.fetcherInFlight = queryFetcher; + [queryFetcher beginFetchWithDelegate:self + didFinishSelector:@selector(queryFetcher:finishedWithData:error:)]; +} + +- (void)queryFetcher:(GTMSessionFetcher *)queryFetcher + finishedWithData:(NSData *)data + error:(NSError *)error { + self.fetcherInFlight = nil; + + NSDictionary *responseHeaders = [queryFetcher responseHeaders]; + NSString *sizeReceivedHeader; + + GTMSessionUploadFetcherStatus uploadStatus = + [[self class] uploadStatusFromResponseHeaders:responseHeaders]; + GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown || error != nil, + @"query fetcher completion has unexpected upload status for headers %@", responseHeaders); + + if (error == nil) { + sizeReceivedHeader = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadSizeReceived]; + + if (uploadStatus == kStatusCancelled || + (uploadStatus == kStatusActive && sizeReceivedHeader == nil)) { + NSDictionary *userInfo = nil; + if (data.length > 0) { + userInfo = @{ kGTMSessionFetcherStatusDataKey : data }; + } + error = [self prematureFailureErrorWithUserInfo:userInfo]; + } + } + + if (error == nil) { + int64_t offset = [sizeReceivedHeader longLongValue]; + int64_t fullUploadLength = [self fullUploadLength]; + if (uploadStatus == kStatusFinal || + (offset >= fullUploadLength && + fullUploadLength != kGTMSessionUploadFetcherUnknownFileSize)) { + // Handle we're done + [self chunkFetcher:queryFetcher finishedWithData:data error:nil]; + } else { + [self retrieveUploadChunkGranularityFromResponseHeaders:responseHeaders]; + [self uploadNextChunkWithOffset:offset]; + } + } else { + // Handle query error + [self chunkFetcher:queryFetcher finishedWithData:data error:error]; + } +} + +- (void)sendCancelUploadWithFetcherProperties:(NSDictionary *)props { + @synchronized(self) { + _isCancelInFlight = YES; + } + GTMSessionFetcher *cancelFetcher = [self uploadFetcherWithProperties:props + isQueryFetch:YES]; + cancelFetcher.bodyData = [NSData data]; + + NSString *originalComment = self.comment; + [cancelFetcher setCommentWithFormat:@"%@ (cancel)", + originalComment ? originalComment : @"upload"]; + + [cancelFetcher setRequestValue:@"cancel" forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand]; + + self.fetcherInFlight = cancelFetcher; + [cancelFetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { + self.fetcherInFlight = nil; + if (![self triggerCancellationHandlerForFetch:cancelFetcher data:data error:error]) { + if (error) { + GTMSESSION_LOG_DEBUG(@"cancelFetcher %@", error); + } + } + @synchronized(self) { + self->_isCancelInFlight = NO; + } + }]; +} + +- (void)uploadNextChunkWithOffset:(int64_t)offset + fetcherProperties:(NSDictionary *)props { + GTMSessionCheckNotSynchronized(self); + + // Example chunk headers: + // X-Goog-Upload-Command: upload, finalize + // X-Goog-Upload-Offset: 0 + // Content-Length: 2000000 + // Content-Type: image/jpeg + // + // {bytes 0-1999999} + + // The chunk upload URL requires no authentication header. + GTMSessionFetcher *chunkFetcher = [self uploadFetcherWithProperties:props + isQueryFetch:NO]; + [self attachSendProgressBlockToChunkFetcher:chunkFetcher]; + int64_t chunkSize = [self updateChunkFetcher:chunkFetcher + forChunkAtOffset:offset]; + BOOL isUploadingFileURL = (self.uploadFileURL != nil); + int64_t fullUploadLength = [self fullUploadLength]; + + // The chunk size may have changed, so determine again if we're uploading the full file. + BOOL isUploadingFullFile = (offset == 0 && + fullUploadLength != kGTMSessionUploadFetcherUnknownFileSize && + chunkSize >= fullUploadLength); + if (isUploadingFullFile && isUploadingFileURL) { + // The data is the full upload file URL. + chunkFetcher.bodyFileURL = self.uploadFileURL; + [self beginChunkFetcher:chunkFetcher + offset:offset]; + } else { + // Make an NSData for the subset for this upload chunk. + self.subdataGenerating = YES; + [self generateChunkSubdataWithOffset:offset + length:chunkSize + response:^(NSData *chunkData, int64_t uploadFileLength, NSError *chunkError) { + // The subdata methods may leave us on a background thread. + dispatch_async(dispatch_get_main_queue(), ^{ + self.subdataGenerating = NO; + + // dont allow the updating of fileLength for uploads not using a data provider as they + // should know the file length before the upload starts. + if (self->_uploadDataProvider != nil && uploadFileLength > 0) { + [self setUploadFileLength:uploadFileLength]; + // Update the command and content-length headers if this is the last chunk to be sent. + if (offset + chunkSize >= uploadFileLength) { + int64_t updatedChunkSize = [self updateChunkFetcher:chunkFetcher + forChunkAtOffset:offset]; + if (updatedChunkSize == 0) { + // Calling beginChunkFetcher early when there is no more data to send allows us to + // properly handle nil chunkData below without having to account for the case where + // we are just finalizing the file. + chunkFetcher.bodyData = [[NSData alloc] init]; + [self beginChunkFetcher:chunkFetcher + offset:offset]; + return; + } + } + } + + if (chunkData == nil) { + NSError *responseError = chunkError; + if (!responseError) { + responseError = [self uploadChunkUnavailableErrorWithDescription:@"chunkData is nil"]; + } + [self invokeFinalCallbackWithData:nil + error:responseError + shouldInvalidateLocation:YES]; + return; + } + + BOOL didWriteFile = NO; + if (isUploadingFileURL) { + // Make a temporary file with the data subset. + NSString *tempName = + [NSString stringWithFormat:@"GTMUpload_temp_%@", [[NSUUID UUID] UUIDString]]; + NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempName]; + NSError *writeError; + didWriteFile = [chunkData writeToFile:tempPath + options:NSDataWritingAtomic + error:&writeError]; + if (didWriteFile) { + chunkFetcher.bodyFileURL = [NSURL fileURLWithPath:tempPath]; + } else { + GTMSESSION_LOG_DEBUG(@"writeToFile failed: %@\n%@", writeError, tempPath); + } + } + if (!didWriteFile) { + chunkFetcher.bodyData = [chunkData copy]; + } + [self beginChunkFetcher:chunkFetcher + offset:offset]; + }); + }]; + } +} + +- (void)beginChunkFetcher:(GTMSessionFetcher *)chunkFetcher + offset:(int64_t)offset { + + // Track the current offset for progress reporting + self.currentOffset = offset; + + // Hang on to the fetcher in case we need to cancel it. We set these before beginning the + // chunk fetch so the observers notified of chunk fetches can inspect the upload fetcher to + // match to the chunk. + self.chunkFetcher = chunkFetcher; + self.fetcherInFlight = chunkFetcher; + + // Update the last chunk request, including any request headers. + self.lastChunkRequest = chunkFetcher.request; + + [chunkFetcher beginFetchWithDelegate:self + didFinishSelector:@selector(chunkFetcher:finishedWithData:error:)]; +} + +- (void)attachSendProgressBlockToChunkFetcher:(GTMSessionFetcher *)chunkFetcher { + chunkFetcher.sendProgressBlock = ^(int64_t bytesSent, int64_t totalBytesSent, + int64_t totalBytesExpectedToSend) { + // The total bytes expected include the initial body and the full chunked + // data, independent of how big this fetcher's chunk is. + int64_t initialBodySent = [self bodyLength]; // TODO(grobbins) use [self initialBodySent] + int64_t totalSent = initialBodySent + self.currentOffset + totalBytesSent; + int64_t totalExpected = initialBodySent + [self fullUploadLength]; + + [self invokeDelegateWithDidSendBytes:bytesSent + totalBytesSent:totalSent + totalBytesExpectedToSend:totalExpected]; + }; +} + +- (NSDictionary *)uploadSessionIdentifierMetadata { + NSMutableDictionary *metadata = [NSMutableDictionary dictionary]; + metadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] = @YES; + GTMSESSION_ASSERT_DEBUG(self.uploadFileURL, + @"Invalid upload fetcher to create session identifier for metadata"); + metadata[kGTMSessionIdentifierUploadFileURLMetadataKey] = [self.uploadFileURL absoluteString]; + metadata[kGTMSessionIdentifierUploadFileLengthMetadataKey] = @([self fullUploadLength]); + + if (self.uploadLocationURL) { + metadata[kGTMSessionIdentifierUploadLocationURLMetadataKey] = + [self.uploadLocationURL absoluteString]; + } + if (self.uploadMIMEType) { + metadata[kGTMSessionIdentifierUploadMIMETypeMetadataKey] = self.uploadMIMEType; + } + metadata[kGTMSessionIdentifierUploadChunkSizeMetadataKey] = @(self.chunkSize); + metadata[kGTMSessionIdentifierUploadCurrentOffsetMetadataKey] = @(self.currentOffset); + return metadata; +} + +- (GTMSessionFetcher *)uploadFetcherWithProperties:(NSDictionary *)properties + isQueryFetch:(BOOL)isQueryFetch { + GTMSessionCheckNotSynchronized(self); + + // Common code to make a request for a query command or for a chunk upload. + NSURL *uploadLocationURL = self.uploadLocationURL; + NSMutableURLRequest *chunkRequest = [NSMutableURLRequest requestWithURL:uploadLocationURL]; + [chunkRequest setHTTPMethod:@"PUT"]; + + // copy the user-agent from the original connection + // n.b. that self.request is nil for upload fetchers created with an existing upload location + // URL. + NSURLRequest *origRequest = self.request; + NSString *userAgent = [origRequest valueForHTTPHeaderField:@"User-Agent"]; + if (userAgent.length > 0) { + [chunkRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + + [chunkRequest setValue:kGTMSessionXGoogUploadProtocolResumable + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadProtocol]; + + // To avoid timeouts when debugging, copy the timeout of the initial fetcher. + NSTimeInterval origTimeout = [origRequest timeoutInterval]; + [chunkRequest setTimeoutInterval:origTimeout]; + + // + // Make a new chunk fetcher. + // + GTMSessionFetcher *chunkFetcher = [GTMSessionFetcher fetcherWithRequest:chunkRequest]; + chunkFetcher.callbackQueue = self.callbackQueue; + chunkFetcher.sessionUserInfo = self.sessionUserInfo; + chunkFetcher.configurationBlock = self.configurationBlock; + chunkFetcher.allowedInsecureSchemes = self.allowedInsecureSchemes; + chunkFetcher.allowLocalhostRequest = self.allowLocalhostRequest; + chunkFetcher.allowInvalidServerCertificates = self.allowInvalidServerCertificates; + chunkFetcher.useUploadTask = !isQueryFetch; + + if (self.uploadFileURL && !isQueryFetch && self.useBackgroundSession) { + [chunkFetcher createSessionIdentifierWithMetadata:[self uploadSessionIdentifierMetadata]]; + } + + // Give the chunk fetcher the same properties as the previous chunk fetcher + chunkFetcher.properties = [properties mutableCopy]; + [chunkFetcher setProperty:[NSValue valueWithNonretainedObject:self] + forKey:kGTMSessionUploadFetcherChunkParentKey]; + + // copy other fetcher settings to the new fetcher + chunkFetcher.retryEnabled = self.retryEnabled; + chunkFetcher.maxRetryInterval = self.maxRetryInterval; + + if ([self isRetryEnabled]) { + // We interpose our own retry method both so we can change the request to ask the server to + // tell us where to resume the chunk. + chunkFetcher.retryBlock = ^(BOOL suggestedWillRetry, NSError *chunkError, + GTMSessionFetcherRetryResponse response) { + void (^finish)(BOOL) = ^(BOOL shouldRetry){ + // We'll retry by sending an offset query. + if (shouldRetry) { + self.shouldInitiateOffsetQuery = !isQueryFetch; + + // We don't know what our actual offset is anymore, but the server will tell us. + self.currentOffset = 0; + } + // We don't actually want to retry this specific fetcher. + response(NO); + }; + + GTMSessionFetcherRetryBlock retryBlock = self.retryBlock; + if (retryBlock) { + // Ask the client, then call the finish block above. + retryBlock(suggestedWillRetry, chunkError, finish); + } else { + finish(suggestedWillRetry); + } + }; + } + + return chunkFetcher; +} + +- (void)chunkFetcher:(GTMSessionFetcher *)chunkFetcher + finishedWithData:(NSData *)data + error:(NSError *)error { + BOOL hasDestroyedOldChunkFetcher = NO; + self.fetcherInFlight = nil; + + NSDictionary *responseHeaders = [chunkFetcher responseHeaders]; + GTMSessionUploadFetcherStatus uploadStatus = + [[self class] uploadStatusFromResponseHeaders:responseHeaders]; + GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown + || error != nil + || self.wasCreatedFromBackgroundSession, + @"chunk fetcher completion has kStatusUnknown upload status for headers %@ fetcher %@", + responseHeaders, self); + BOOL isUploadStatusStopped = (uploadStatus == kStatusFinal || uploadStatus == kStatusCancelled); + + // Check if the fetcher was actually querying. If it failed, do not retry, + // as it would enter an infinite retry loop. + NSString *uploadCommand = + chunkFetcher.request.allHTTPHeaderFields[kGTMSessionHeaderXGoogUploadCommand]; + BOOL isQueryFetch = [uploadCommand isEqual:@"query"]; + + // TODO + // Maybe here we can check to see if the request had x goog content length set. (the file length one). + int64_t previousContentLength = + [[chunkFetcher.request valueForHTTPHeaderField:@"Content-Length"] longLongValue]; + // The Content-Length header may not be present if the chunk fetcher was recreated from + // a background session. + BOOL hasKnownChunkSize = (previousContentLength > 0); + BOOL needsQuery = (!hasKnownChunkSize && !isUploadStatusStopped); + + if (error || (needsQuery && !isQueryFetch)) { + NSInteger status = error.code; + + // Status 4xx indicates a bad offset in the Google upload protocol. However, do not retry status + // 404 per spec, nor if the upload size appears to have been zero (since the server will just + // keep asking us to retry.) + if (self.shouldInitiateOffsetQuery || + (needsQuery && !isQueryFetch) || + ([error.domain isEqual:kGTMSessionFetcherStatusDomain] && + status >= 400 && status <= 499 && + status != 404 && + uploadStatus == kStatusActive && + previousContentLength > 0)) { + self.shouldInitiateOffsetQuery = NO; + [self destroyChunkFetcher]; + hasDestroyedOldChunkFetcher = YES; + [self sendQueryForUploadOffsetWithFetcherProperties:chunkFetcher.properties]; + } else { + // Some unexpected status has occurred; handle it as we would a regular + // object fetcher failure. + [self invokeFinalCallbackWithData:data + error:error + shouldInvalidateLocation:NO]; + } + } else { + // The chunk has uploaded successfully. + int64_t newOffset = self.currentOffset + previousContentLength; +#if DEBUG + // Verify that if we think all of the uploading data has been sent, the server responded with + // the "final" upload status. + BOOL hasUploadAllData = (newOffset == [self fullUploadLength]); + BOOL isFinalStatus = (uploadStatus == kStatusFinal); + #pragma unused(hasUploadAllData,isFinalStatus) + GTMSESSION_ASSERT_DEBUG(hasUploadAllData == isFinalStatus || !hasKnownChunkSize, + @"uploadStatus:%@ newOffset:%lld (%lld + %lld) fullUploadLength:%lld" + @" chunkFetcher:%@ requestHeaders:%@ responseHeaders:%@", + [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus], + newOffset, self.currentOffset, previousContentLength, + [self fullUploadLength], + chunkFetcher, chunkFetcher.request.allHTTPHeaderFields, + responseHeaders); +#endif + if (isUploadStatusStopped || (_currentOffset > _uploadFileLength && _uploadFileLength > 0)) { + // This was the last chunk. + if (error == nil && uploadStatus == kStatusCancelled) { + // Report cancelled status as an error. + NSDictionary *userInfo = nil; + if (data.length > 0) { + userInfo = @{ kGTMSessionFetcherStatusDataKey : data }; + } + data = nil; + error = [self prematureFailureErrorWithUserInfo:userInfo]; + } else { + // The upload is in final status. + // + // Take the chunk fetcher's data as the superclass data. + self.downloadedData = data; + self.statusCode = chunkFetcher.statusCode; + } + + // we're done + [self invokeFinalCallbackWithData:data + error:error + shouldInvalidateLocation:YES]; + } else { + // Start the next chunk. + self.currentOffset = newOffset; + + // We want to destroy this chunk fetcher before creating the next one, but + // we want to pass on its properties + NSDictionary *props = [chunkFetcher properties]; + + // We no longer need to be able to cancel this chunkFetcher. Destroy it + // before we create a new chunk fetcher. + [self destroyChunkFetcher]; + hasDestroyedOldChunkFetcher = YES; + + [self uploadNextChunkWithOffset:newOffset + fetcherProperties:props]; + } + } + if (!hasDestroyedOldChunkFetcher) { + [self destroyChunkFetcher]; + } +} + +- (void)destroyChunkFetcher { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_fetcherInFlight == _chunkFetcher) { + _fetcherInFlight = nil; + } + + [_chunkFetcher stopFetching]; + + NSURL *chunkFileURL = _chunkFetcher.bodyFileURL; + BOOL wasTemporaryUploadFile = ![chunkFileURL isEqual:_uploadFileURL]; + if (wasTemporaryUploadFile) { + NSError *error; + [[NSFileManager defaultManager] removeItemAtURL:chunkFileURL + error:&error]; + if (error) { + GTMSESSION_LOG_DEBUG(@"removingItemAtURL failed: %@\n%@", error, chunkFileURL); + } + } + + _recentChunkReponseHeaders = _chunkFetcher.responseHeaders; + + // To avoid retain cycles, remove all properties except the parent identifier. + _chunkFetcher.properties = + @{ kGTMSessionUploadFetcherChunkParentKey : [NSValue valueWithNonretainedObject:self] }; + + _chunkFetcher.retryBlock = nil; + _chunkFetcher.sendProgressBlock = nil; + _chunkFetcher = nil; + } // @synchronized(self) +} + +// This method calculates the proper values to pass to the client's send progress block. +// +// The actual total bytes sent include the initial body sent, plus the +// offset into the batched data prior to the current chunk fetcher + +- (void)invokeDelegateWithDidSendBytes:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent + totalBytesExpectedToSend:(int64_t)totalBytesExpected { + GTMSessionCheckNotSynchronized(self); + + // Ensure the chunk fetcher survives the callback in case the user pauses the upload process. + __block GTMSessionFetcher *holdFetcher = self.chunkFetcher; + + [self invokeOnCallbackQueue:self.delegateCallbackQueue + afterUserStopped:NO + block:^{ + GTMSessionFetcherSendProgressBlock sendProgressBlock = self.sendProgressBlock; + if (sendProgressBlock) { + sendProgressBlock(bytesSent, totalBytesSent, totalBytesExpected); + } + holdFetcher = nil; + }]; +} + +- (void)retrieveUploadChunkGranularityFromResponseHeaders:(NSDictionary *)responseHeaders { + GTMSessionCheckNotSynchronized(self); + + // Standard granularity for Google uploads is 256K. + NSString *chunkGranularityHeader = + [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadChunkGranularity]; + self.uploadGranularity = chunkGranularityHeader.longLongValue; +} + +#pragma mark - + +- (BOOL)isPaused { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _isPaused; + } // @synchronized(self) +} + +- (void)pauseFetching { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _isPaused = YES; + } // @synchronized(self) + + // Pausing just means stopping the current chunk from uploading; + // when we resume, we will send a query request to the server to + // figure out what bytes to resume sending. + // + // We won't try to cancel the initial data upload, but rather will check + // for being paused in beginChunkFetches. + [self destroyChunkFetcher]; +} + +- (void)resumeFetching { + BOOL wasPaused; + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + wasPaused = _isPaused; + _isPaused = NO; + } // @synchronized(self) + + if (wasPaused) { + [self sendQueryForUploadOffsetWithFetcherProperties:self.properties]; + } +} + +- (void)stopFetching { + // Overrides the superclass + [self destroyChunkFetcher]; + + // If we think the server is waiting for more data, then tell it there won't be more. + if (self.uploadLocationURL) { + [self sendCancelUploadWithFetcherProperties:[self properties]]; + self.uploadLocationURL = nil; + } else { + [self invokeOnCallbackQueue:self.callbackQueue + afterUserStopped:YES + block:^{ + // Repeated calls to stopFetching may cause this path to be reached despite having sent a real + // cancel request, check here to ensure that the cancellation handler invocation which fires + // will definitely be for the real request sent previously. + @synchronized(self) { + if (self->_isCancelInFlight) { + return; + } + } + [self triggerCancellationHandlerForFetch:nil data:nil error:nil]; + }]; + } + + [super stopFetching]; +} + +// Fires the cancellation handler, returning whether there was a handler to be fired. +- (BOOL)triggerCancellationHandlerForFetch:(GTMSessionFetcher *)fetcher + data:(NSData *)data + error:(NSError *)error { + GTMSessionUploadFetcherCancellationHandler handler = self.cancellationHandler; + if (handler) { + handler(fetcher, data, error); + self.cancellationHandler = nil; + return YES; + } + return NO; +} + +#pragma mark - + +- (int64_t)updateChunkFetcher:(GTMSessionFetcher *)chunkFetcher + forChunkAtOffset:(int64_t)offset { + BOOL isUploadingFileURL = (self.uploadFileURL != nil); + + // Upload another chunk, meeting server-required granularity. + int64_t chunkSize = self.chunkSize; + + int64_t fullUploadLength = [self fullUploadLength]; + BOOL isFileLengthKnown = fullUploadLength >= 0; + + BOOL isUploadingFullFile = (offset == 0 && isFileLengthKnown && chunkSize >= fullUploadLength); + if (!isUploadingFileURL || !isUploadingFullFile) { + // We're not uploading the entire file and given the file URL. Since we'll be + // allocating a subdata block for a chunk, we need to bound it to something that + // won't blow the process's memory. + if (chunkSize > kGTMSessionUploadFetcherMaximumDemandBufferSize) { + chunkSize = kGTMSessionUploadFetcherMaximumDemandBufferSize; + } + } + + int64_t granularity = self.uploadGranularity; + if (granularity > 0) { + if (chunkSize < granularity) { + chunkSize = granularity; + } else { + chunkSize = chunkSize - (chunkSize % granularity); + } + } + + GTMSESSION_ASSERT_DEBUG(offset < fullUploadLength || fullUploadLength == 0, + @"offset %lld exceeds data length %lld", offset, fullUploadLength); + + if (granularity > 0) { + offset = offset - (offset % granularity); + } + + // If the chunk size is bigger than the remaining data, or else + // it's close enough in size to the remaining data that we'd rather + // avoid having a whole extra http fetch for the leftover bit, then make + // this chunk size exactly match the remaining data size + NSString *command; + int64_t thisChunkSize = chunkSize; + + BOOL isChunkTooBig = (thisChunkSize >= (fullUploadLength - offset)); + BOOL isChunkAlmostBigEnough = (fullUploadLength - offset - 2500 < thisChunkSize); + BOOL isFinalChunk = (isChunkTooBig || isChunkAlmostBigEnough) && isFileLengthKnown; + if (isFinalChunk) { + thisChunkSize = fullUploadLength - offset; + if (thisChunkSize > 0) { + command = @"upload, finalize"; + } else { + command = @"finalize"; + } + } else { + command = @"upload"; + } + NSString *lengthStr = @(thisChunkSize).stringValue; + NSString *offsetStr = @(offset).stringValue; + + [chunkFetcher setRequestValue:command forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand]; + [chunkFetcher setRequestValue:lengthStr forHTTPHeaderField:@"Content-Length"]; + [chunkFetcher setRequestValue:offsetStr forHTTPHeaderField:kGTMSessionHeaderXGoogUploadOffset]; + if (_uploadFileLength != kGTMSessionUploadFetcherUnknownFileSize) { + [chunkFetcher setRequestValue:@([self fullUploadLength]).stringValue + forHTTPHeaderField:kGTMSessionHeaderXGoogUploadContentLength]; + } + + // Append the range of bytes in this chunk to the fetcher comment. + NSString *baseComment = self.comment; + [chunkFetcher setCommentWithFormat:@"%@ (%lld-%lld)", + baseComment ? baseComment : @"upload", offset, MAX(0, offset + thisChunkSize - 1)]; + + return thisChunkSize; +} + +// Public properties. +@synthesize currentOffset = _currentOffset, + delegateCompletionHandler = _delegateCompletionHandler, + chunkFetcher = _chunkFetcher, + lastChunkRequest = _lastChunkRequest, + subdataGenerating = _subdataGenerating, + shouldInitiateOffsetQuery = _shouldInitiateOffsetQuery, + uploadGranularity = _uploadGranularity; + +// Internal properties. +@dynamic fetcherInFlight; +@dynamic activeFetcher; +@dynamic statusCode; +@dynamic delegateCallbackQueue; + ++ (void)removePointer:(void *)pointer fromPointerArray:(NSPointerArray *)pointerArray { + for (NSUInteger index = 0, count = pointerArray.count; index < count; ++index) { + void *pointerAtIndex = [pointerArray pointerAtIndex:index]; + if (pointerAtIndex == pointer) { + [pointerArray removePointerAtIndex:index]; + return; + } + } +} + +- (BOOL)useBackgroundSession { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _useBackgroundSessionOnChunkFetchers; + } // @synchronized(self +} + +- (void)setUseBackgroundSession:(BOOL)useBackgroundSession { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_useBackgroundSessionOnChunkFetchers != useBackgroundSession) { + _useBackgroundSessionOnChunkFetchers = useBackgroundSession; + NSPointerArray *uploadFetcherPointerArrayForBackgroundSessions = + [[self class] uploadFetcherPointerArrayForBackgroundSessions]; + if (_useBackgroundSessionOnChunkFetchers) { + [uploadFetcherPointerArrayForBackgroundSessions addPointer:(__bridge void *)self]; + } else { + [[self class] removePointer:(__bridge void *)self + fromPointerArray:uploadFetcherPointerArrayForBackgroundSessions]; + } + } + } // @synchronized(self +} + +- (BOOL)canFetchWithBackgroundSession { + // The initial upload fetcher is always a foreground session; the + // useBackgroundSession property will apply only to chunk fetchers, + // not to queries. + return NO; +} + +- (NSDictionary *)responseHeaders { + GTMSessionCheckNotSynchronized(self); + // Overrides the superclass + + // If asked for the fetcher's response, use the most recent chunk fetcher's response, + // since the original request's response lacks useful information like the actual + // Content-Type. + NSDictionary *dict = self.chunkFetcher.responseHeaders; + if (dict) { + return dict; + } + + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + if (_recentChunkReponseHeaders) { + return _recentChunkReponseHeaders; + } + } // @synchronized(self + + // No chunk fetcher yet completed, so return whatever we have from the initial fetch. + return [super responseHeaders]; +} + +- (NSInteger)statusCodeUnsynchronized { + GTMSessionCheckSynchronized(self); + + if (_recentChunkStatusCode != -1) { + // Overrides the superclass to indicate status appropriate to the initial + // or latest chunk fetch + return _recentChunkStatusCode; + } else { + return [super statusCodeUnsynchronized]; + } +} + + +- (void)setStatusCode:(NSInteger)val { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _recentChunkStatusCode = val; + } +} + +- (int64_t)initialBodyLength { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _initialBodyLength; + } +} + +- (void)setInitialBodyLength:(int64_t)length { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _initialBodyLength = length; + } +} + +- (int64_t)initialBodySent { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _initialBodySent; + } +} + +- (void)setInitialBodySent:(int64_t)length { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _initialBodySent = length; + } +} + +- (NSURL *)uploadLocationURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + return _uploadLocationURL; + } +} + +- (void)setUploadLocationURL:(NSURL *)locationURL { + @synchronized(self) { + GTMSessionMonitorSynchronized(self); + + _uploadLocationURL = locationURL; + } +} + +- (GTMSessionFetcher *)activeFetcher { + GTMSessionFetcher *result = self.fetcherInFlight; + if (result) return result; + + return self; +} + +- (BOOL)isFetching { + // If there is an active chunk fetcher, then the upload fetcher is considered + // to still be fetching. + if (self.fetcherInFlight != nil) return YES; + + return [super isFetching]; +} + +- (BOOL)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds { + NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + + while (self.fetcherInFlight || self.subdataGenerating) { + if ([timeoutDate timeIntervalSinceNow] < 0) return NO; + + if (self.subdataGenerating) { + // Allow time for subdata generation. + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } else { + // Wait for any chunk or query fetchers that still have pending callbacks or + // notifications. + BOOL timedOut; + + if (self.fetcherInFlight == self) { + timedOut = ![super waitForCompletionWithTimeout:timeoutInSeconds]; + } else { + timedOut = ![self.fetcherInFlight waitForCompletionWithTimeout:timeoutInSeconds]; + } + if (timedOut) return NO; + } + } + return YES; +} + +@end + +@implementation GTMSessionFetcher (GTMSessionUploadFetcherMethods) + +- (GTMSessionUploadFetcher *)parentUploadFetcher { + NSValue *property = [self propertyForKey:kGTMSessionUploadFetcherChunkParentKey]; + if (!property) return nil; + + GTMSessionUploadFetcher *uploadFetcher = property.nonretainedObjectValue; + + GTMSESSION_ASSERT_DEBUG([uploadFetcher isKindOfClass:[GTMSessionUploadFetcher class]], + @"Unexpected parent upload fetcher class: %@", [uploadFetcher class]); + return uploadFetcher; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/LICENSE b/Pods/GoogleAPIClientForREST/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Pods/GoogleAPIClientForREST/README.md b/Pods/GoogleAPIClientForREST/README.md @@ -0,0 +1,48 @@ +# Google APIs Client Library for Objective-C for REST # + +**Project site** <https://github.com/google/google-api-objectivec-client-for-rest><br> +**Discussion group** <http://groups.google.com/group/google-api-objectivec-client> + +[![Build Status](https://travis-ci.org/google/google-api-objectivec-client-for-rest.svg?branch=master)](https://travis-ci.org/google/google-api-objectivec-client-for-rest) + +Written by Google, this library is a flexible and efficient Objective-C +framework for accessing JSON APIs. + +This is the recommended library for accessing JSON-based Google APIs for iOS and +Mac OS X applications. The library is compatible with applications built for +iOS 7 and later, and Mac OS X 10.9 and later. + +**To get started** with Google APIs and the Objective-C client library, Read the +[wiki](https://github.com/google/google-api-objectivec-client-for-rest/wiki). +See +[BuildingTheLibrary](https://github.com/google/google-api-objectivec-client-for-rest/wiki/BuildingTheLibrary) +for how to add the library to a Mac or iPhone application project, it covers +directly adding sources or using CocoaPods. Study the +[example applications](https://github.com/google/google-api-objectivec-client-for-rest/tree/master/Examples). + +Generated interfaces for Google APIs are in the +[GeneratedServices folder](https://github.com/google/google-api-objectivec-client-for-rest/tree/master/Source/GeneratedServices). + +In addition to the pre generated classes included with the library, you can +generate your own source for other services that have a +[discovery document](https://developers.google.com/discovery/v1/reference/apis#resource-representations) +by using the +[ServiceGenerator](https://github.com/google/google-api-objectivec-client-for-rest/wiki/ServiceGenerator). + +**If you have a problem** or want a new feature to be included in the library, +please join the +[discussion group](http://groups.google.com/group/google-api-objectivec-client). +Be sure to include +[http logs](https://github.com/google/google-api-objectivec-client-for-rest/wiki#logging-http-server-traffic) +for requests and responses when posting questions. Bugs may also be submitted +on the [issues list](https://github.com/google/google-api-objectivec-client-for-rest/issues). + +**Externally-included projects**: The library includes code from the separate +projects [GTM Session Fetcher](https://github.com/google/gtm-session-fetcher), +[GTMAppAuth](https://github.com/google/GTMAppAuth). + +**Google Data APIs**: The much older library for XML-based APIs is +[still available](https://github.com/google/gdata-objectivec-client). + +Other useful classes for Mac and iOS developers are available in the +[Google Toolbox for Mac](https://github.com/google/google-toolbox-for-mac). diff --git a/Pods/GoogleAPIClientForREST/Source/GTLRDefines.h b/Pods/GoogleAPIClientForREST/Source/GTLRDefines.h @@ -0,0 +1,109 @@ +/* Copyright (c) 2011 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. + */ + +// +// GTLRDefines.h +// + +// Ensure Apple's conditionals we depend on are defined. +#import <TargetConditionals.h> +#import <AvailabilityMacros.h> + +// These can be redefined via a prefix if you are prefixing symbols to prefix +// the names used in strings. Something like: +// #define _HELPER(x) "MyPrefix" #x +// #define GTLR_CLASSNAME_STR(x) @_HELPER(x) +// #define GTLR_CLASSNAME_CSTR(x) _HELPER(x) +#ifndef GTLR_CLASSNAME_STR + #define _GTLR_CLASSNAME_HELPER(x) #x + #define GTLR_CLASSNAME_STR(x) @_GTLR_CLASSNAME_HELPER(x) + #define GTLR_CLASSNAME_CSTR(x) _GTLR_CLASSNAME_HELPER(x) +#endif + +// Provide a common definition for externing constants/functions +#if defined(__cplusplus) + #define GTLR_EXTERN extern "C" +#else + #define GTLR_EXTERN extern +#endif + +// +// GTLR_ASSERT defaults to bridging to NSAssert. This macro exists just in case +// it needs to be remapped. +// GTLR_DEBUG_ASSERT is similar, but compiles in only for debug builds +// + +#ifndef GTLR_ASSERT + // NSCAssert to avoid capturing self if used in a block. + #define GTLR_ASSERT(condition, ...) NSCAssert(condition, __VA_ARGS__) +#endif // GTLR_ASSERT + +#ifndef GTLR_DEBUG_ASSERT + #if DEBUG && !defined(NS_BLOCK_ASSERTIONS) + #define GTLR_DEBUG_ASSERT(condition, ...) GTLR_ASSERT(condition, __VA_ARGS__) + #elif DEBUG + // In DEBUG builds with assertions blocked, log to avoid unused variable warnings. + #define GTLR_DEBUG_ASSERT(condition, ...) if (!(condition)) { NSLog(__VA_ARGS__); } + #else + #define GTLR_DEBUG_ASSERT(condition, ...) do { } while (0) + #endif +#endif + +#ifndef GTLR_DEBUG_LOG + #if DEBUG + #define GTLR_DEBUG_LOG(...) NSLog(__VA_ARGS__) + #else + #define GTLR_DEBUG_LOG(...) do { } while (0) + #endif +#endif + +#ifndef GTLR_DEBUG_ASSERT_CURRENT_QUEUE + #define GTLR_ASSERT_CURRENT_QUEUE_DEBUG(targetQueue) \ + GTLR_DEBUG_ASSERT(0 == strcmp(GTLR_QUEUE_NAME(targetQueue), \ + GTLR_QUEUE_NAME(DISPATCH_CURRENT_QUEUE_LABEL)), \ + @"Current queue is %s (expected %s)", \ + GTLR_QUEUE_NAME(DISPATCH_CURRENT_QUEUE_LABEL), \ + GTLR_QUEUE_NAME(targetQueue)) + + #define GTLR_QUEUE_NAME(queue) \ + (strlen(dispatch_queue_get_label(queue)) > 0 ? dispatch_queue_get_label(queue) : "unnamed") +#endif // GTLR_ASSERT_CURRENT_QUEUE_DEBUG + +// Sanity check the min versions. + +#if (defined(TARGET_OS_TV) && TARGET_OS_TV) || (defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) + // No min checks for these two. +#elif TARGET_OS_IPHONE + #if !defined(__IPHONE_9_0) || (__IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0) + #error "This project expects to be compiled with the iOS 9.0 SDK (or later)." + #endif + #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 + #error "The minimum supported iOS version is 7.0." + #endif +#elif TARGET_OS_MAC + #if !defined(MAC_OS_X_VERSION_10_10) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) + #error "This project expects to be compiled with the OS X 10.10 SDK (or later)." + #endif + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 + #error "The minimum supported OS X version is 10.9." + #endif +#else + #error "Unknown target platform." +#endif + +// Version marker used to validate the generated sources against the library +// version. The will be changed any time the library makes a change that means +// past sources need to be regenerated. +#define GTLR_RUNTIME_VERSION 3000 diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheets.h b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheets.h @@ -0,0 +1,13 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#import "GTLRSheetsObjects.h" +#import "GTLRSheetsQuery.h" +#import "GTLRSheetsService.h" diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.h b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.h @@ -0,0 +1,9245 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#if GTLR_BUILT_AS_FRAMEWORK + #import "GTLR/GTLRObject.h" +#else + #import "GTLRObject.h" +#endif + +#if GTLR_RUNTIME_VERSION != 3000 +#error This file was generated by a different version of ServiceGenerator which is incompatible with this GTLR library source. +#endif + +@class GTLRSheets_AddBandingRequest; +@class GTLRSheets_AddBandingResponse; +@class GTLRSheets_AddChartRequest; +@class GTLRSheets_AddChartResponse; +@class GTLRSheets_AddConditionalFormatRuleRequest; +@class GTLRSheets_AddDimensionGroupRequest; +@class GTLRSheets_AddDimensionGroupResponse; +@class GTLRSheets_AddFilterViewRequest; +@class GTLRSheets_AddFilterViewResponse; +@class GTLRSheets_AddNamedRangeRequest; +@class GTLRSheets_AddNamedRangeResponse; +@class GTLRSheets_AddProtectedRangeRequest; +@class GTLRSheets_AddProtectedRangeResponse; +@class GTLRSheets_AddSheetRequest; +@class GTLRSheets_AddSheetResponse; +@class GTLRSheets_AppendCellsRequest; +@class GTLRSheets_AppendDimensionRequest; +@class GTLRSheets_AutoFillRequest; +@class GTLRSheets_AutoResizeDimensionsRequest; +@class GTLRSheets_BandedRange; +@class GTLRSheets_BandingProperties; +@class GTLRSheets_BasicChartAxis; +@class GTLRSheets_BasicChartDomain; +@class GTLRSheets_BasicChartSeries; +@class GTLRSheets_BasicChartSpec; +@class GTLRSheets_BasicFilter; +@class GTLRSheets_BasicFilter_Criteria; +@class GTLRSheets_BooleanCondition; +@class GTLRSheets_BooleanRule; +@class GTLRSheets_Border; +@class GTLRSheets_Borders; +@class GTLRSheets_BubbleChartSpec; +@class GTLRSheets_CandlestickChartSpec; +@class GTLRSheets_CandlestickData; +@class GTLRSheets_CandlestickDomain; +@class GTLRSheets_CandlestickSeries; +@class GTLRSheets_CellData; +@class GTLRSheets_CellFormat; +@class GTLRSheets_ChartData; +@class GTLRSheets_ChartSourceRange; +@class GTLRSheets_ChartSpec; +@class GTLRSheets_ClearBasicFilterRequest; +@class GTLRSheets_Color; +@class GTLRSheets_ConditionalFormatRule; +@class GTLRSheets_ConditionValue; +@class GTLRSheets_CopyPasteRequest; +@class GTLRSheets_CreateDeveloperMetadataRequest; +@class GTLRSheets_CreateDeveloperMetadataResponse; +@class GTLRSheets_CutPasteRequest; +@class GTLRSheets_DataFilter; +@class GTLRSheets_DataFilterValueRange; +@class GTLRSheets_DataValidationRule; +@class GTLRSheets_DateTimeRule; +@class GTLRSheets_DeleteBandingRequest; +@class GTLRSheets_DeleteConditionalFormatRuleRequest; +@class GTLRSheets_DeleteConditionalFormatRuleResponse; +@class GTLRSheets_DeleteDeveloperMetadataRequest; +@class GTLRSheets_DeleteDeveloperMetadataResponse; +@class GTLRSheets_DeleteDimensionGroupRequest; +@class GTLRSheets_DeleteDimensionGroupResponse; +@class GTLRSheets_DeleteDimensionRequest; +@class GTLRSheets_DeleteEmbeddedObjectRequest; +@class GTLRSheets_DeleteFilterViewRequest; +@class GTLRSheets_DeleteNamedRangeRequest; +@class GTLRSheets_DeleteProtectedRangeRequest; +@class GTLRSheets_DeleteRangeRequest; +@class GTLRSheets_DeleteSheetRequest; +@class GTLRSheets_DeveloperMetadata; +@class GTLRSheets_DeveloperMetadataLocation; +@class GTLRSheets_DeveloperMetadataLookup; +@class GTLRSheets_DimensionGroup; +@class GTLRSheets_DimensionProperties; +@class GTLRSheets_DimensionRange; +@class GTLRSheets_DuplicateFilterViewRequest; +@class GTLRSheets_DuplicateFilterViewResponse; +@class GTLRSheets_DuplicateSheetRequest; +@class GTLRSheets_DuplicateSheetResponse; +@class GTLRSheets_Editors; +@class GTLRSheets_EmbeddedChart; +@class GTLRSheets_EmbeddedObjectPosition; +@class GTLRSheets_ErrorValue; +@class GTLRSheets_ExtendedValue; +@class GTLRSheets_FilterCriteria; +@class GTLRSheets_FilterView; +@class GTLRSheets_FilterView_Criteria; +@class GTLRSheets_FindReplaceRequest; +@class GTLRSheets_FindReplaceResponse; +@class GTLRSheets_GradientRule; +@class GTLRSheets_GridCoordinate; +@class GTLRSheets_GridData; +@class GTLRSheets_GridProperties; +@class GTLRSheets_GridRange; +@class GTLRSheets_HistogramChartSpec; +@class GTLRSheets_HistogramRule; +@class GTLRSheets_HistogramSeries; +@class GTLRSheets_InsertDimensionRequest; +@class GTLRSheets_InsertRangeRequest; +@class GTLRSheets_InterpolationPoint; +@class GTLRSheets_IterativeCalculationSettings; +@class GTLRSheets_LineStyle; +@class GTLRSheets_ManualRule; +@class GTLRSheets_ManualRuleGroup; +@class GTLRSheets_MatchedDeveloperMetadata; +@class GTLRSheets_MatchedValueRange; +@class GTLRSheets_MergeCellsRequest; +@class GTLRSheets_MoveDimensionRequest; +@class GTLRSheets_NamedRange; +@class GTLRSheets_NumberFormat; +@class GTLRSheets_OrgChartSpec; +@class GTLRSheets_OverlayPosition; +@class GTLRSheets_Padding; +@class GTLRSheets_PasteDataRequest; +@class GTLRSheets_PieChartSpec; +@class GTLRSheets_PivotFilterCriteria; +@class GTLRSheets_PivotGroup; +@class GTLRSheets_PivotGroupRule; +@class GTLRSheets_PivotGroupSortValueBucket; +@class GTLRSheets_PivotGroupValueMetadata; +@class GTLRSheets_PivotTable; +@class GTLRSheets_PivotTable_Criteria; +@class GTLRSheets_PivotValue; +@class GTLRSheets_ProtectedRange; +@class GTLRSheets_RandomizeRangeRequest; +@class GTLRSheets_RepeatCellRequest; +@class GTLRSheets_Request; +@class GTLRSheets_Response; +@class GTLRSheets_RowData; +@class GTLRSheets_SetBasicFilterRequest; +@class GTLRSheets_SetDataValidationRequest; +@class GTLRSheets_Sheet; +@class GTLRSheets_SheetProperties; +@class GTLRSheets_SortRangeRequest; +@class GTLRSheets_SortSpec; +@class GTLRSheets_SourceAndDestination; +@class GTLRSheets_Spreadsheet; +@class GTLRSheets_SpreadsheetProperties; +@class GTLRSheets_TextFormat; +@class GTLRSheets_TextFormatRun; +@class GTLRSheets_TextPosition; +@class GTLRSheets_TextRotation; +@class GTLRSheets_TextToColumnsRequest; +@class GTLRSheets_TreemapChartColorScale; +@class GTLRSheets_TreemapChartSpec; +@class GTLRSheets_UnmergeCellsRequest; +@class GTLRSheets_UpdateBandingRequest; +@class GTLRSheets_UpdateBordersRequest; +@class GTLRSheets_UpdateCellsRequest; +@class GTLRSheets_UpdateChartSpecRequest; +@class GTLRSheets_UpdateConditionalFormatRuleRequest; +@class GTLRSheets_UpdateConditionalFormatRuleResponse; +@class GTLRSheets_UpdateDeveloperMetadataRequest; +@class GTLRSheets_UpdateDeveloperMetadataResponse; +@class GTLRSheets_UpdateDimensionGroupRequest; +@class GTLRSheets_UpdateDimensionPropertiesRequest; +@class GTLRSheets_UpdateEmbeddedObjectPositionRequest; +@class GTLRSheets_UpdateEmbeddedObjectPositionResponse; +@class GTLRSheets_UpdateFilterViewRequest; +@class GTLRSheets_UpdateNamedRangeRequest; +@class GTLRSheets_UpdateProtectedRangeRequest; +@class GTLRSheets_UpdateSheetPropertiesRequest; +@class GTLRSheets_UpdateSpreadsheetPropertiesRequest; +@class GTLRSheets_UpdateValuesByDataFilterResponse; +@class GTLRSheets_UpdateValuesResponse; +@class GTLRSheets_ValueRange; +@class GTLRSheets_WaterfallChartColumnStyle; +@class GTLRSheets_WaterfallChartCustomSubtotal; +@class GTLRSheets_WaterfallChartDomain; +@class GTLRSheets_WaterfallChartSeries; +@class GTLRSheets_WaterfallChartSpec; + +// Generated comments include content from the discovery document; avoid them +// causing warnings since clang's checks are some what arbitrary. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +NS_ASSUME_NONNULL_BEGIN + +// ---------------------------------------------------------------------------- +// Constants - For some of the classes' properties below. + +// ---------------------------------------------------------------------------- +// GTLRSheets_AppendDimensionRequest.dimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartAxis.position + +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_AXIS_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartAxis_Position_BasicChartAxisPositionUnspecified; +/** + * The axis rendered at the bottom of a chart. + * For most charts, this is the standard major axis. + * For bar charts, this is a minor axis. + * + * Value: "BOTTOM_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartAxis_Position_BottomAxis; +/** + * The axis rendered at the left of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is the standard major axis. + * + * Value: "LEFT_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartAxis_Position_LeftAxis; +/** + * The axis rendered at the right of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is an unusual major axis. + * + * Value: "RIGHT_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartAxis_Position_RightAxis; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSeries.targetAxis + +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_AXIS_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_BasicChartAxisPositionUnspecified; +/** + * The axis rendered at the bottom of a chart. + * For most charts, this is the standard major axis. + * For bar charts, this is a minor axis. + * + * Value: "BOTTOM_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_BottomAxis; +/** + * The axis rendered at the left of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is the standard major axis. + * + * Value: "LEFT_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_LeftAxis; +/** + * The axis rendered at the right of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is an unusual major axis. + * + * Value: "RIGHT_AXIS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_RightAxis; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSeries.type + +/** + * An <a href="/chart/interactive/docs/gallery/areachart">area chart</a>. + * + * Value: "AREA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Area; +/** + * A <a href="/chart/interactive/docs/gallery/barchart">bar chart</a>. + * + * Value: "BAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Bar; +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_BasicChartTypeUnspecified; +/** + * A <a href="/chart/interactive/docs/gallery/columnchart">column chart</a>. + * + * Value: "COLUMN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Column; +/** + * A <a href="/chart/interactive/docs/gallery/combochart">combo chart</a>. + * + * Value: "COMBO" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Combo; +/** + * A <a href="/chart/interactive/docs/gallery/linechart">line chart</a>. + * + * Value: "LINE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Line; +/** + * A <a href="/chart/interactive/docs/gallery/scatterchart">scatter + * chart</a>. + * + * Value: "SCATTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_Scatter; +/** + * A <a href="/chart/interactive/docs/gallery/steppedareachart">stepped area + * chart</a>. + * + * Value: "STEPPED_AREA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSeries_Type_SteppedArea; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSpec.chartType + +/** + * An <a href="/chart/interactive/docs/gallery/areachart">area chart</a>. + * + * Value: "AREA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Area; +/** + * A <a href="/chart/interactive/docs/gallery/barchart">bar chart</a>. + * + * Value: "BAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Bar; +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_BasicChartTypeUnspecified; +/** + * A <a href="/chart/interactive/docs/gallery/columnchart">column chart</a>. + * + * Value: "COLUMN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Column; +/** + * A <a href="/chart/interactive/docs/gallery/combochart">combo chart</a>. + * + * Value: "COMBO" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Combo; +/** + * A <a href="/chart/interactive/docs/gallery/linechart">line chart</a>. + * + * Value: "LINE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Line; +/** + * A <a href="/chart/interactive/docs/gallery/scatterchart">scatter + * chart</a>. + * + * Value: "SCATTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_Scatter; +/** + * A <a href="/chart/interactive/docs/gallery/steppedareachart">stepped area + * chart</a>. + * + * Value: "STEPPED_AREA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_ChartType_SteppedArea; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSpec.compareMode + +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_COMPARE_MODE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_CompareMode_BasicChartCompareModeUnspecified; +/** + * All data elements with the same category (e.g., domain value) are + * highlighted and shown in the tooltip. + * + * Value: "CATEGORY" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_CompareMode_Category; +/** + * Only the focused data element is highlighted and shown in the tooltip. + * + * Value: "DATUM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_CompareMode_Datum; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSpec.legendPosition + +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_LEGEND_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_BasicChartLegendPositionUnspecified; +/** + * The legend is rendered on the bottom of the chart. + * + * Value: "BOTTOM_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_BottomLegend; +/** + * The legend is rendered on the left of the chart. + * + * Value: "LEFT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_LeftLegend; +/** + * No legend is rendered. + * + * Value: "NO_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_NoLegend; +/** + * The legend is rendered on the right of the chart. + * + * Value: "RIGHT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_RightLegend; +/** + * The legend is rendered on the top of the chart. + * + * Value: "TOP_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_TopLegend; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BasicChartSpec.stackedType + +/** + * Default value, do not use. + * + * Value: "BASIC_CHART_STACKED_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_StackedType_BasicChartStackedTypeUnspecified; +/** + * Series are not stacked. + * + * Value: "NOT_STACKED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_StackedType_NotStacked; +/** + * Vertical stacks are stretched to reach the top of the chart, with + * values laid out as percentages of each other. + * + * Value: "PERCENT_STACKED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_StackedType_PercentStacked; +/** + * Series values are stacked, each value is rendered vertically beginning + * from the top of the value below it. + * + * Value: "STACKED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BasicChartSpec_StackedType_Stacked; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchGetValuesByDataFilterRequest.dateTimeRenderOption + +/** + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). + * + * Value: "FORMATTED_STRING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_FormattedString; +/** + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. + * + * Value: "SERIAL_NUMBER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_SerialNumber; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchGetValuesByDataFilterRequest.majorDimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchGetValuesByDataFilterRequest.valueRenderOption + +/** + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return `"$1.23"`. + * + * Value: "FORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_FormattedValue; +/** + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then A2 would return `"=A1"`. + * + * Value: "FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_Formula; +/** + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return the number `1.23`. + * + * Value: "UNFORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_UnformattedValue; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.responseDateTimeRenderOption + +/** + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). + * + * Value: "FORMATTED_STRING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_FormattedString; +/** + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. + * + * Value: "SERIAL_NUMBER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_SerialNumber; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.responseValueRenderOption + +/** + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return `"$1.23"`. + * + * Value: "FORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_FormattedValue; +/** + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then A2 would return `"=A1"`. + * + * Value: "FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_Formula; +/** + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return the number `1.23`. + * + * Value: "UNFORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_UnformattedValue; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.valueInputOption + +/** + * Default input value. This value must not be used. + * + * Value: "INPUT_VALUE_OPTION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_InputValueOptionUnspecified; +/** + * The values the user has entered will not be parsed and will be stored + * as-is. + * + * Value: "RAW" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_Raw; +/** + * The values will be parsed as if the user typed them into the UI. + * Numbers will stay as numbers, but strings may be converted to numbers, + * dates, etc. following the same rules that are applied when entering + * text into a cell via the Google Sheets UI. + * + * Value: "USER_ENTERED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_UserEntered; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesRequest.responseDateTimeRenderOption + +/** + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). + * + * Value: "FORMATTED_STRING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_FormattedString; +/** + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. + * + * Value: "SERIAL_NUMBER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_SerialNumber; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesRequest.responseValueRenderOption + +/** + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return `"$1.23"`. + * + * Value: "FORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_FormattedValue; +/** + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then A2 would return `"=A1"`. + * + * Value: "FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_Formula; +/** + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as currency, + * then `A2` would return the number `1.23`. + * + * Value: "UNFORMATTED_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_UnformattedValue; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BatchUpdateValuesRequest.valueInputOption + +/** + * Default input value. This value must not be used. + * + * Value: "INPUT_VALUE_OPTION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_InputValueOptionUnspecified; +/** + * The values the user has entered will not be parsed and will be stored + * as-is. + * + * Value: "RAW" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_Raw; +/** + * The values will be parsed as if the user typed them into the UI. + * Numbers will stay as numbers, but strings may be converted to numbers, + * dates, etc. following the same rules that are applied when entering + * text into a cell via the Google Sheets UI. + * + * Value: "USER_ENTERED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_UserEntered; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BooleanCondition.type + +/** + * The cell's value must be empty. + * Supported by conditional formatting and filters. + * Requires no ConditionValues. + * + * Value: "BLANK" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_Blank; +/** + * The cell's value must be TRUE/FALSE or in the list of condition values. + * Supported by data validation. + * Renders as a cell checkbox. + * Supports zero, one or two ConditionValues. No + * values indicates the cell must be TRUE or FALSE, where TRUE renders as + * checked and FALSE renders as unchecked. One value indicates the cell + * will render as checked when it contains that value and unchecked when it + * is blank. Two values indicate that the cell will render as checked when + * it contains the first value and unchecked when it contains the second + * value. For example, ["Yes","No"] indicates that the cell will render a + * checked box when it has the value "Yes" and an unchecked box when it has + * the value "No". + * + * Value: "BOOLEAN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_Boolean; +/** + * The default value, do not use. + * + * Value: "CONDITION_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_ConditionTypeUnspecified; +/** + * The condition's formula must evaluate to true. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "CUSTOM_FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_CustomFormula; +/** + * The cell's value must be after the date of the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue + * that may be a relative date. + * + * Value: "DATE_AFTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateAfter; +/** + * The cell's value must be before the date of the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue + * that may be a relative date. + * + * Value: "DATE_BEFORE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateBefore; +/** + * The cell's value must be between the dates of the two condition values. + * Supported by data validation. + * Requires exactly two ConditionValues. + * + * Value: "DATE_BETWEEN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateBetween; +/** + * The cell's value must be the same date as the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "DATE_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateEq; +/** + * The cell's value must be a date. + * Supported by data validation. + * Requires no ConditionValues. + * + * Value: "DATE_IS_VALID" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateIsValid; +/** + * The cell's value must be outside the dates of the two condition values. + * Supported by data validation. + * Requires exactly two ConditionValues. + * + * Value: "DATE_NOT_BETWEEN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateNotBetween; +/** + * The cell's value must be on or after the date of the condition's value. + * Supported by data validation. + * Requires a single ConditionValue + * that may be a relative date. + * + * Value: "DATE_ON_OR_AFTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateOnOrAfter; +/** + * The cell's value must be on or before the date of the condition's value. + * Supported by data validation. + * Requires a single ConditionValue + * that may be a relative date. + * + * Value: "DATE_ON_OR_BEFORE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_DateOnOrBefore; +/** + * The cell's value must not be empty. + * Supported by conditional formatting and filters. + * Requires no ConditionValues. + * + * Value: "NOT_BLANK" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NotBlank; +/** + * The cell's value must be between the two condition values. + * Supported by data validation, conditional formatting and filters. + * Requires exactly two ConditionValues. + * + * Value: "NUMBER_BETWEEN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberBetween; +/** + * The cell's value must be equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberEq; +/** + * The cell's value must be greater than the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_GREATER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberGreater; +/** + * The cell's value must be greater than or equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_GREATER_THAN_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberGreaterThanEq; +/** + * The cell's value must be less than the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_LESS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberLess; +/** + * The cell's value must be less than or equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_LESS_THAN_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberLessThanEq; +/** + * The cell's value must not be between the two condition values. + * Supported by data validation, conditional formatting and filters. + * Requires exactly two ConditionValues. + * + * Value: "NUMBER_NOT_BETWEEN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberNotBetween; +/** + * The cell's value must be not equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "NUMBER_NOT_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_NumberNotEq; +/** + * The cell's value must be in the list of condition values. + * Supported by data validation. + * Supports any number of condition values, + * one per item in the list. + * Formulas are not supported in the values. + * + * Value: "ONE_OF_LIST" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_OneOfList; +/** + * The cell's value must be listed in the grid in condition value's range. + * Supported by data validation. + * Requires a single ConditionValue, + * and the value must be a valid range in A1 notation. + * + * Value: "ONE_OF_RANGE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_OneOfRange; +/** + * The cell's value must contain the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "TEXT_CONTAINS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextContains; +/** + * The cell's value must end with the condition's value. + * Supported by conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "TEXT_ENDS_WITH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextEndsWith; +/** + * The cell's value must be exactly the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "TEXT_EQ" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextEq; +/** + * The cell's value must be a valid email address. + * Supported by data validation. + * Requires no ConditionValues. + * + * Value: "TEXT_IS_EMAIL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextIsEmail; +/** + * The cell's value must be a valid URL. + * Supported by data validation. + * Requires no ConditionValues. + * + * Value: "TEXT_IS_URL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextIsUrl; +/** + * The cell's value must not contain the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "TEXT_NOT_CONTAINS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextNotContains; +/** + * The cell's value must start with the condition's value. + * Supported by conditional formatting and filters. + * Requires a single ConditionValue. + * + * Value: "TEXT_STARTS_WITH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BooleanCondition_Type_TextStartsWith; + +// ---------------------------------------------------------------------------- +// GTLRSheets_Border.style + +/** + * The border is dashed. + * + * Value: "DASHED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_Dashed; +/** + * The border is dotted. + * + * Value: "DOTTED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_Dotted; +/** + * The border is two solid lines. + * + * Value: "DOUBLE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_Double; +/** + * No border. + * Used only when updating a border in order to erase it. + * + * Value: "NONE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_None; +/** + * The border is a thin solid line. + * + * Value: "SOLID" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_Solid; +/** + * The border is a medium solid line. + * + * Value: "SOLID_MEDIUM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_SolidMedium; +/** + * The border is a thick solid line. + * + * Value: "SOLID_THICK" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_SolidThick; +/** + * The style is not specified. Do not use this. + * + * Value: "STYLE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_Border_Style_StyleUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_BubbleChartSpec.legendPosition + +/** + * The legend is rendered on the bottom of the chart. + * + * Value: "BOTTOM_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_BottomLegend; +/** + * Default value, do not use. + * + * Value: "BUBBLE_CHART_LEGEND_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_BubbleChartLegendPositionUnspecified; +/** + * The legend is rendered inside the chart area. + * + * Value: "INSIDE_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_InsideLegend; +/** + * The legend is rendered on the left of the chart. + * + * Value: "LEFT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_LeftLegend; +/** + * No legend is rendered. + * + * Value: "NO_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_NoLegend; +/** + * The legend is rendered on the right of the chart. + * + * Value: "RIGHT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_RightLegend; +/** + * The legend is rendered on the top of the chart. + * + * Value: "TOP_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_TopLegend; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CellFormat.horizontalAlignment + +/** + * The text is explicitly aligned to the center of the cell. + * + * Value: "CENTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Center; +/** + * The horizontal alignment is not specified. Do not use this. + * + * Value: "HORIZONTAL_ALIGN_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_HorizontalAlignUnspecified; +/** + * The text is explicitly aligned to the left of the cell. + * + * Value: "LEFT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Left; +/** + * The text is explicitly aligned to the right of the cell. + * + * Value: "RIGHT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Right; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CellFormat.hyperlinkDisplayType + +/** + * The default value: the hyperlink is rendered. Do not use this. + * + * Value: "HYPERLINK_DISPLAY_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_HyperlinkDisplayTypeUnspecified; +/** + * A hyperlink should be explicitly rendered. + * + * Value: "LINKED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_Linked; +/** + * A hyperlink should not be rendered. + * + * Value: "PLAIN_TEXT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_PlainText; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CellFormat.textDirection + +/** + * The text direction of left-to-right was set by the user. + * + * Value: "LEFT_TO_RIGHT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_TextDirection_LeftToRight; +/** + * The text direction of right-to-left was set by the user. + * + * Value: "RIGHT_TO_LEFT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_TextDirection_RightToLeft; +/** + * The text direction is not specified. Do not use this. + * + * Value: "TEXT_DIRECTION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_TextDirection_TextDirectionUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CellFormat.verticalAlignment + +/** + * The text is explicitly aligned to the bottom of the cell. + * + * Value: "BOTTOM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Bottom; +/** + * The text is explicitly aligned to the middle of the cell. + * + * Value: "MIDDLE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Middle; +/** + * The text is explicitly aligned to the top of the cell. + * + * Value: "TOP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Top; +/** + * The vertical alignment is not specified. Do not use this. + * + * Value: "VERTICAL_ALIGN_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_VerticalAlignment_VerticalAlignUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CellFormat.wrapStrategy + +/** + * Lines that are longer than the cell width will be clipped. + * The text will never wrap to the next line unless the user manually + * inserts a new line. + * Example: + * | First sentence. | + * | Manual newline t| <- Text is clipped + * | Next newline. | + * + * Value: "CLIP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_WrapStrategy_Clip; +/** + * This wrap strategy represents the old Google Sheets wrap strategy where + * words that are longer than a line are clipped rather than broken. This + * strategy is not supported on all platforms and is being phased out. + * Example: + * | Cell has a | + * | loooooooooo| <- Word is clipped. + * | word. | + * + * Value: "LEGACY_WRAP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_WrapStrategy_LegacyWrap; +/** + * Lines that are longer than the cell width will be written in the next + * cell over, so long as that cell is empty. If the next cell over is + * non-empty, this behaves the same as CLIP. The text will never wrap + * to the next line unless the user manually inserts a new line. + * Example: + * | First sentence. | + * | Manual newline that is very long. <- Text continues into next cell + * | Next newline. | + * + * Value: "OVERFLOW_CELL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_WrapStrategy_OverflowCell; +/** + * Words that are longer than a line are wrapped at the character level + * rather than clipped. + * Example: + * | Cell has a | + * | loooooooooo| <- Word is broken. + * | ong word. | + * + * Value: "WRAP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_WrapStrategy_Wrap; +/** + * The default value, do not use. + * + * Value: "WRAP_STRATEGY_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CellFormat_WrapStrategy_WrapStrategyUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_ChartSpec.hiddenDimensionStrategy + +/** + * Default value, do not use. + * + * Value: "CHART_HIDDEN_DIMENSION_STRATEGY_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ChartHiddenDimensionStrategyUnspecified; +/** + * Charts will not skip any hidden rows or columns. + * + * Value: "SHOW_ALL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ShowAll; +/** + * Charts will skip hidden columns only. + * + * Value: "SKIP_HIDDEN_COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenColumns; +/** + * Charts will skip hidden rows only. + * + * Value: "SKIP_HIDDEN_ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRows; +/** + * Charts will skip hidden rows and columns. + * + * Value: "SKIP_HIDDEN_ROWS_AND_COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRowsAndColumns; + +// ---------------------------------------------------------------------------- +// GTLRSheets_ConditionValue.relativeDate + +/** + * The value is one month before today. + * + * Value: "PAST_MONTH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastMonth; +/** + * The value is one week before today. + * + * Value: "PAST_WEEK" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastWeek; +/** + * The value is one year before today. + * + * Value: "PAST_YEAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastYear; +/** + * Default value, do not use. + * + * Value: "RELATIVE_DATE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_RelativeDateUnspecified; +/** + * The value is today. + * + * Value: "TODAY" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_Today; +/** + * The value is tomorrow. + * + * Value: "TOMORROW" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_Tomorrow; +/** + * The value is yesterday. + * + * Value: "YESTERDAY" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ConditionValue_RelativeDate_Yesterday; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CopyPasteRequest.pasteOrientation + +/** + * Paste normally. + * + * Value: "NORMAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteOrientation_Normal; +/** + * Paste transposed, where all rows become columns and vice versa. + * + * Value: "TRANSPOSE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteOrientation_Transpose; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CopyPasteRequest.pasteType + +/** + * Paste the conditional formatting rules only. + * + * Value: "PASTE_CONDITIONAL_FORMATTING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteConditionalFormatting; +/** + * Paste the data validation only. + * + * Value: "PASTE_DATA_VALIDATION" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteDataValidation; +/** + * Paste the format and data validation only. + * + * Value: "PASTE_FORMAT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteFormat; +/** + * Paste the formulas only. + * + * Value: "PASTE_FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteFormula; +/** + * Like PASTE_NORMAL but without borders. + * + * Value: "PASTE_NO_BORDERS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteNoBorders; +/** + * Paste values, formulas, formats, and merges. + * + * Value: "PASTE_NORMAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteNormal; +/** + * Paste the values ONLY without formats, formulas, or merges. + * + * Value: "PASTE_VALUES" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteValues; + +// ---------------------------------------------------------------------------- +// GTLRSheets_CutPasteRequest.pasteType + +/** + * Paste the conditional formatting rules only. + * + * Value: "PASTE_CONDITIONAL_FORMATTING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteConditionalFormatting; +/** + * Paste the data validation only. + * + * Value: "PASTE_DATA_VALIDATION" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteDataValidation; +/** + * Paste the format and data validation only. + * + * Value: "PASTE_FORMAT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteFormat; +/** + * Paste the formulas only. + * + * Value: "PASTE_FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteFormula; +/** + * Like PASTE_NORMAL but without borders. + * + * Value: "PASTE_NO_BORDERS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteNoBorders; +/** + * Paste values, formulas, formats, and merges. + * + * Value: "PASTE_NORMAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteNormal; +/** + * Paste the values ONLY without formats, formulas, or merges. + * + * Value: "PASTE_VALUES" + */ +GTLR_EXTERN NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteValues; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DataFilterValueRange.majorDimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DateTimeRule.type + +/** + * The default type, do not use. + * + * Value: "DATE_TIME_RULE_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_DateTimeRuleTypeUnspecified; +/** + * Group dates by day and month, for example 22-Nov. The month is + * translated based on the spreadsheet locale. + * + * Value: "DAY_MONTH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_DayMonth; +/** + * Group dates by day of month, from 1 to 31. + * + * Value: "DAY_OF_MONTH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_DayOfMonth; +/** + * Group dates by day of week, for example Sunday. The days of the week will + * be translated based on the spreadsheet locale. + * + * Value: "DAY_OF_WEEK" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_DayOfWeek; +/** + * Group dates by day of year, from 1 to 366. Note that dates after Feb. 29 + * fall in different buckets in leap years than in non-leap years. + * + * Value: "DAY_OF_YEAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_DayOfYear; +/** + * Group dates by hour using a 24-hour system, from 0 to 23. + * + * Value: "HOUR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Hour; +/** + * Group dates by hour and minute using a 24-hour system, for example 19:45. + * + * Value: "HOUR_MINUTE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_HourMinute; +/** + * Group dates by hour and minute using a 12-hour system, for example 7:45 + * PM. The AM/PM designation is translated based on the spreadsheet + * locale. + * + * Value: "HOUR_MINUTE_AMPM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_HourMinuteAmpm; +/** + * Group dates by minute, from 0 to 59. + * + * Value: "MINUTE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Minute; +/** + * Group dates by month, for example Nov. The month is translated based + * on the spreadsheet locale. + * + * Value: "MONTH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Month; +/** + * Group dates by quarter, for example Q1 (which represents Jan-Mar). + * + * Value: "QUARTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Quarter; +/** + * Group dates by second, from 0 to 59. + * + * Value: "SECOND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Second; +/** + * Group dates by year, for example 2008. + * + * Value: "YEAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_Year; +/** + * Group dates by year and month, for example 2008-Nov. The month is + * translated based on the spreadsheet locale. + * + * Value: "YEAR_MONTH" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_YearMonth; +/** + * Group dates by year, month, and day, for example 2008-11-22. + * + * Value: "YEAR_MONTH_DAY" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_YearMonthDay; +/** + * Group dates by year and quarter, for example 2008 Q4. + * + * Value: "YEAR_QUARTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DateTimeRule_Type_YearQuarter; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeleteRangeRequest.shiftDimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeveloperMetadata.visibility + +/** + * Default value. + * + * Value: "DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadata_Visibility_DeveloperMetadataVisibilityUnspecified; +/** + * Document-visible metadata is accessible from any developer project with + * access to the document. + * + * Value: "DOCUMENT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadata_Visibility_Document; +/** + * Project-visible metadata is only visible to and accessible by the developer + * project that created the metadata. + * + * Value: "PROJECT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadata_Visibility_Project; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeveloperMetadataLocation.locationType + +/** + * Developer metadata associated on an entire column dimension. + * + * Value: "COLUMN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Column; +/** + * Default value. + * + * Value: "DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_DeveloperMetadataLocationTypeUnspecified; +/** + * Developer metadata associated on an entire row dimension. + * + * Value: "ROW" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Row; +/** + * Developer metadata associated on an entire sheet. + * + * Value: "SHEET" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Sheet; +/** + * Developer metadata associated on the entire spreadsheet. + * + * Value: "SPREADSHEET" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Spreadsheet; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeveloperMetadataLookup.locationMatchingStrategy + +/** + * Default value. This value must not be used. + * + * Value: "DEVELOPER_METADATA_LOCATION_MATCHING_STRATEGY_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_DeveloperMetadataLocationMatchingStrategyUnspecified; +/** + * Indicates that a specified location should be matched exactly. For + * example, if row three were specified as a location this matching strategy + * would only match developer metadata also associated on row three. Metadata + * associated on other locations would not be considered. + * + * Value: "EXACT_LOCATION" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_ExactLocation; +/** + * Indicates that a specified location should match that exact location as + * well as any intersecting locations. For example, if row three were + * specified as a location this matching strategy would match developer + * metadata associated on row three as well as metadata associated on + * locations that intersect row three. If, for instance, there was developer + * metadata associated on column B, this matching strategy would also match + * that location because column B intersects row three. + * + * Value: "INTERSECTING_LOCATION" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_IntersectingLocation; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeveloperMetadataLookup.locationType + +/** + * Developer metadata associated on an entire column dimension. + * + * Value: "COLUMN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Column; +/** + * Default value. + * + * Value: "DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_DeveloperMetadataLocationTypeUnspecified; +/** + * Developer metadata associated on an entire row dimension. + * + * Value: "ROW" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Row; +/** + * Developer metadata associated on an entire sheet. + * + * Value: "SHEET" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Sheet; +/** + * Developer metadata associated on the entire spreadsheet. + * + * Value: "SPREADSHEET" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Spreadsheet; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DeveloperMetadataLookup.visibility + +/** + * Default value. + * + * Value: "DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_DeveloperMetadataVisibilityUnspecified; +/** + * Document-visible metadata is accessible from any developer project with + * access to the document. + * + * Value: "DOCUMENT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_Document; +/** + * Project-visible metadata is only visible to and accessible by the developer + * project that created the metadata. + * + * Value: "PROJECT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_Project; + +// ---------------------------------------------------------------------------- +// GTLRSheets_DimensionRange.dimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DimensionRange_Dimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DimensionRange_Dimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_DimensionRange_Dimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_ErrorValue.type + +/** + * Corresponds to the `#DIV/0` error. + * + * Value: "DIVIDE_BY_ZERO" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_DivideByZero; +/** + * Corresponds to the `#ERROR!` error. + * + * Value: "ERROR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Error; +/** + * The default error type, do not use this. + * + * Value: "ERROR_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_ErrorTypeUnspecified; +/** + * Corresponds to the `Loading...` state. + * + * Value: "LOADING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Loading; +/** + * Corresponds to the `#N/A` error. + * + * Value: "N_A" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_NA; +/** + * Corresponds to the `#NAME?` error. + * + * Value: "NAME" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Name; +/** + * Corresponds to the `#NULL!` error. + * + * Value: "NULL_VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_NullValue; +/** + * Corresponds to the `#NUM`! error. + * + * Value: "NUM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Num; +/** + * Corresponds to the `#REF!` error. + * + * Value: "REF" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Ref; +/** + * Corresponds to the `#VALUE!` error. + * + * Value: "VALUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ErrorValue_Type_Value; + +// ---------------------------------------------------------------------------- +// GTLRSheets_HistogramChartSpec.legendPosition + +/** + * The legend is rendered on the bottom of the chart. + * + * Value: "BOTTOM_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_BottomLegend; +/** + * Default value, do not use. + * + * Value: "HISTOGRAM_CHART_LEGEND_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_HistogramChartLegendPositionUnspecified; +/** + * The legend is rendered inside the chart area. + * + * Value: "INSIDE_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_InsideLegend; +/** + * The legend is rendered on the left of the chart. + * + * Value: "LEFT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_LeftLegend; +/** + * No legend is rendered. + * + * Value: "NO_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_NoLegend; +/** + * The legend is rendered on the right of the chart. + * + * Value: "RIGHT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_RightLegend; +/** + * The legend is rendered on the top of the chart. + * + * Value: "TOP_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_TopLegend; + +// ---------------------------------------------------------------------------- +// GTLRSheets_InsertRangeRequest.shiftDimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_InterpolationPoint.type + +/** + * The default value, do not use. + * + * Value: "INTERPOLATION_POINT_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_InterpolationPointTypeUnspecified; +/** + * The interpolation point uses the maximum value in the + * cells over the range of the conditional format. + * + * Value: "MAX" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_Max; +/** + * The interpolation point uses the minimum value in the + * cells over the range of the conditional format. + * + * Value: "MIN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_Min; +/** + * The interpolation point uses exactly the value in + * InterpolationPoint.value. + * + * Value: "NUMBER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_Number; +/** + * The interpolation point is the given percentage over + * all the cells in the range of the conditional format. + * This is equivalent to NUMBER if the value was: + * `=(MAX(FLATTEN(range)) * (value / 100)) + * + (MIN(FLATTEN(range)) * (1 - (value / 100)))` + * (where errors in the range are ignored when flattening). + * + * Value: "PERCENT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_Percent; +/** + * The interpolation point is the given percentile + * over all the cells in the range of the conditional format. + * This is equivalent to NUMBER if the value was: + * `=PERCENTILE(FLATTEN(range), value / 100)` + * (where errors in the range are ignored when flattening). + * + * Value: "PERCENTILE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_InterpolationPoint_Type_Percentile; + +// ---------------------------------------------------------------------------- +// GTLRSheets_LineStyle.type + +/** + * A custom dash for a line. Modifying the exact custom dash style is + * currently unsupported. + * + * Value: "CUSTOM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_Custom; +/** + * A dotted line. + * + * Value: "DOTTED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_Dotted; +/** + * No dash type, which is equivalent to a non-visible line. + * + * Value: "INVISIBLE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_Invisible; +/** + * Default value, do not use. + * + * Value: "LINE_DASH_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_LineDashTypeUnspecified; +/** + * A dashed line where the dashes have "long" length. + * + * Value: "LONG_DASHED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_LongDashed; +/** + * A line that alternates between a "long" dash and a dot. + * + * Value: "LONG_DASHED_DOTTED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_LongDashedDotted; +/** + * A dashed line where the dashes have "medium" length. + * + * Value: "MEDIUM_DASHED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_MediumDashed; +/** + * A line that alternates between a "medium" dash and a dot. + * + * Value: "MEDIUM_DASHED_DOTTED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_MediumDashedDotted; +/** + * A solid line. + * + * Value: "SOLID" + */ +GTLR_EXTERN NSString * const kGTLRSheets_LineStyle_Type_Solid; + +// ---------------------------------------------------------------------------- +// GTLRSheets_MergeCellsRequest.mergeType + +/** + * Create a single merge from the range + * + * Value: "MERGE_ALL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeAll; +/** + * Create a merge for each column in the range + * + * Value: "MERGE_COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeColumns; +/** + * Create a merge for each row in the range + * + * Value: "MERGE_ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeRows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_NumberFormat.type + +/** + * Currency formatting, e.g `$1,000.12` + * + * Value: "CURRENCY" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Currency; +/** + * Date formatting, e.g `9/26/2008` + * + * Value: "DATE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Date; +/** + * Date+Time formatting, e.g `9/26/08 15:59:00` + * + * Value: "DATE_TIME" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_DateTime; +/** + * Number formatting, e.g, `1,000.12` + * + * Value: "NUMBER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Number; +/** + * The number format is not specified + * and is based on the contents of the cell. + * Do not explicitly use this. + * + * Value: "NUMBER_FORMAT_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_NumberFormatTypeUnspecified; +/** + * Percent formatting, e.g `10.12%` + * + * Value: "PERCENT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Percent; +/** + * Scientific number formatting, e.g `1.01E+03` + * + * Value: "SCIENTIFIC" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Scientific; +/** + * Text formatting, e.g `1000.12` + * + * Value: "TEXT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Text; +/** + * Time formatting, e.g `3:59:00 PM` + * + * Value: "TIME" + */ +GTLR_EXTERN NSString * const kGTLRSheets_NumberFormat_Type_Time; + +// ---------------------------------------------------------------------------- +// GTLRSheets_OrgChartSpec.nodeSize + +/** + * The large org chart node size. + * + * Value: "LARGE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Large; +/** + * The medium org chart node size. + * + * Value: "MEDIUM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Medium; +/** + * Default value, do not use. + * + * Value: "ORG_CHART_LABEL_SIZE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_OrgChartSpec_NodeSize_OrgChartLabelSizeUnspecified; +/** + * The small org chart node size. + * + * Value: "SMALL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Small; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PasteDataRequest.type + +/** + * Paste the conditional formatting rules only. + * + * Value: "PASTE_CONDITIONAL_FORMATTING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteConditionalFormatting; +/** + * Paste the data validation only. + * + * Value: "PASTE_DATA_VALIDATION" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteDataValidation; +/** + * Paste the format and data validation only. + * + * Value: "PASTE_FORMAT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteFormat; +/** + * Paste the formulas only. + * + * Value: "PASTE_FORMULA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteFormula; +/** + * Like PASTE_NORMAL but without borders. + * + * Value: "PASTE_NO_BORDERS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteNoBorders; +/** + * Paste values, formulas, formats, and merges. + * + * Value: "PASTE_NORMAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteNormal; +/** + * Paste the values ONLY without formats, formulas, or merges. + * + * Value: "PASTE_VALUES" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PasteDataRequest_Type_PasteValues; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PieChartSpec.legendPosition + +/** + * The legend is rendered on the bottom of the chart. + * + * Value: "BOTTOM_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_BottomLegend; +/** + * Each pie slice has a label attached to it. + * + * Value: "LABELED_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_LabeledLegend; +/** + * The legend is rendered on the left of the chart. + * + * Value: "LEFT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_LeftLegend; +/** + * No legend is rendered. + * + * Value: "NO_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_NoLegend; +/** + * Default value, do not use. + * + * Value: "PIE_CHART_LEGEND_POSITION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_PieChartLegendPositionUnspecified; +/** + * The legend is rendered on the right of the chart. + * + * Value: "RIGHT_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_RightLegend; +/** + * The legend is rendered on the top of the chart. + * + * Value: "TOP_LEGEND" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PieChartSpec_LegendPosition_TopLegend; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PivotGroup.sortOrder + +/** + * Sort ascending. + * + * Value: "ASCENDING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotGroup_SortOrder_Ascending; +/** + * Sort descending. + * + * Value: "DESCENDING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotGroup_SortOrder_Descending; +/** + * Default value, do not use this. + * + * Value: "SORT_ORDER_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotGroup_SortOrder_SortOrderUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PivotTable.valueLayout + +/** + * Values are laid out horizontally (as columns). + * + * Value: "HORIZONTAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotTable_ValueLayout_Horizontal; +/** + * Values are laid out vertically (as rows). + * + * Value: "VERTICAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotTable_ValueLayout_Vertical; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PivotValue.calculatedDisplayType + +/** + * Shows the pivot values as percentage of the column total values. + * + * Value: "PERCENT_OF_COLUMN_TOTAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfColumnTotal; +/** + * Shows the pivot values as percentage of the grand total values. + * + * Value: "PERCENT_OF_GRAND_TOTAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfGrandTotal; +/** + * Shows the pivot values as percentage of the row total values. + * + * Value: "PERCENT_OF_ROW_TOTAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfRowTotal; +/** + * Default value, do not use. + * + * Value: "PIVOT_VALUE_CALCULATED_DISPLAY_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PivotValueCalculatedDisplayTypeUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_PivotValue.summarizeFunction + +/** + * Corresponds to the `AVERAGE` function. + * + * Value: "AVERAGE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Average; +/** + * Corresponds to the `COUNT` function. + * + * Value: "COUNT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Count; +/** + * Corresponds to the `COUNTA` function. + * + * Value: "COUNTA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Counta; +/** + * Corresponds to the `COUNTUNIQUE` function. + * + * Value: "COUNTUNIQUE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Countunique; +/** + * Indicates the formula should be used as-is. + * Only valid if PivotValue.formula was set. + * + * Value: "CUSTOM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Custom; +/** + * Corresponds to the `MAX` function. + * + * Value: "MAX" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Max; +/** + * Corresponds to the `MEDIAN` function. + * + * Value: "MEDIAN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Median; +/** + * Corresponds to the `MIN` function. + * + * Value: "MIN" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Min; +/** + * The default, do not use. + * + * Value: "PIVOT_STANDARD_VALUE_FUNCTION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_PivotStandardValueFunctionUnspecified; +/** + * Corresponds to the `PRODUCT` function. + * + * Value: "PRODUCT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Product; +/** + * Corresponds to the `STDEV` function. + * + * Value: "STDEV" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Stdev; +/** + * Corresponds to the `STDEVP` function. + * + * Value: "STDEVP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Stdevp; +/** + * Corresponds to the `SUM` function. + * + * Value: "SUM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Sum; +/** + * Corresponds to the `VAR` function. + * + * Value: "VAR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Var; +/** + * Corresponds to the `VARP` function. + * + * Value: "VARP" + */ +GTLR_EXTERN NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Varp; + +// ---------------------------------------------------------------------------- +// GTLRSheets_SheetProperties.sheetType + +/** + * The sheet is a grid. + * + * Value: "GRID" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SheetProperties_SheetType_Grid; +/** + * The sheet has no grid and instead has an object like a chart or image. + * + * Value: "OBJECT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SheetProperties_SheetType_Object; +/** + * Default value, do not use. + * + * Value: "SHEET_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SheetProperties_SheetType_SheetTypeUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_SortSpec.sortOrder + +/** + * Sort ascending. + * + * Value: "ASCENDING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SortSpec_SortOrder_Ascending; +/** + * Sort descending. + * + * Value: "DESCENDING" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SortSpec_SortOrder_Descending; +/** + * Default value, do not use this. + * + * Value: "SORT_ORDER_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SortSpec_SortOrder_SortOrderUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_SourceAndDestination.dimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SourceAndDestination_Dimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SourceAndDestination_Dimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SourceAndDestination_Dimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_SpreadsheetProperties.autoRecalc + +/** + * Volatile functions are updated on every change and hourly. + * + * Value: "HOUR" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_Hour; +/** + * Volatile functions are updated on every change and every minute. + * + * Value: "MINUTE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_Minute; +/** + * Volatile functions are updated on every change. + * + * Value: "ON_CHANGE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_OnChange; +/** + * Default value. This value must not be used. + * + * Value: "RECALCULATION_INTERVAL_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_RecalculationIntervalUnspecified; + +// ---------------------------------------------------------------------------- +// GTLRSheets_TextPosition.horizontalAlignment + +/** + * The text is explicitly aligned to the center of the cell. + * + * Value: "CENTER" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Center; +/** + * The horizontal alignment is not specified. Do not use this. + * + * Value: "HORIZONTAL_ALIGN_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_HorizontalAlignUnspecified; +/** + * The text is explicitly aligned to the left of the cell. + * + * Value: "LEFT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Left; +/** + * The text is explicitly aligned to the right of the cell. + * + * Value: "RIGHT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Right; + +// ---------------------------------------------------------------------------- +// GTLRSheets_TextToColumnsRequest.delimiterType + +/** + * Automatically detect columns. + * + * Value: "AUTODETECT" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Autodetect; +/** + * "," + * + * Value: "COMMA" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Comma; +/** + * A custom value as defined in delimiter. + * + * Value: "CUSTOM" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Custom; +/** + * Default value. This value must not be used. + * + * Value: "DELIMITER_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_DelimiterTypeUnspecified; +/** + * "." + * + * Value: "PERIOD" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Period; +/** + * ";" + * + * Value: "SEMICOLON" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Semicolon; +/** + * " " + * + * Value: "SPACE" + */ +GTLR_EXTERN NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Space; + +// ---------------------------------------------------------------------------- +// GTLRSheets_ValueRange.majorDimension + +/** + * Operates on the columns of a sheet. + * + * Value: "COLUMNS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ValueRange_MajorDimension_Columns; +/** + * The default value, do not use. + * + * Value: "DIMENSION_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ValueRange_MajorDimension_DimensionUnspecified; +/** + * Operates on the rows of a sheet. + * + * Value: "ROWS" + */ +GTLR_EXTERN NSString * const kGTLRSheets_ValueRange_MajorDimension_Rows; + +// ---------------------------------------------------------------------------- +// GTLRSheets_WaterfallChartSpec.stackedType + +/** + * Series will spread out along the horizontal axis. + * + * Value: "SEQUENTIAL" + */ +GTLR_EXTERN NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_Sequential; +/** + * Values corresponding to the same domain (horizontal axis) value will be + * stacked vertically. + * + * Value: "STACKED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_Stacked; +/** + * Default value, do not use. + * + * Value: "WATERFALL_STACKED_TYPE_UNSPECIFIED" + */ +GTLR_EXTERN NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_WaterfallStackedTypeUnspecified; + +/** + * Adds a new banded range to the spreadsheet. + */ +@interface GTLRSheets_AddBandingRequest : GTLRObject + +/** + * The banded range to add. The bandedRangeId + * field is optional; if one is not set, an id will be randomly generated. (It + * is an error to specify the ID of a range that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_BandedRange *bandedRange; + +@end + + +/** + * The result of adding a banded range. + */ +@interface GTLRSheets_AddBandingResponse : GTLRObject + +/** The banded range that was added. */ +@property(nonatomic, strong, nullable) GTLRSheets_BandedRange *bandedRange; + +@end + + +/** + * Adds a chart to a sheet in the spreadsheet. + */ +@interface GTLRSheets_AddChartRequest : GTLRObject + +/** + * The chart that should be added to the spreadsheet, including the position + * where it should be placed. The chartId + * field is optional; if one is not set, an id will be randomly generated. (It + * is an error to specify the ID of a chart that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_EmbeddedChart *chart; + +@end + + +/** + * The result of adding a chart to a spreadsheet. + */ +@interface GTLRSheets_AddChartResponse : GTLRObject + +/** The newly added chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_EmbeddedChart *chart; + +@end + + +/** + * Adds a new conditional format rule at the given index. + * All subsequent rules' indexes are incremented. + */ +@interface GTLRSheets_AddConditionalFormatRuleRequest : GTLRObject + +/** + * The zero-based index where the rule should be inserted. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *index; + +/** The rule to add. */ +@property(nonatomic, strong, nullable) GTLRSheets_ConditionalFormatRule *rule; + +@end + + +/** + * Creates a group over the specified range. + * If the requested range is a superset of the range of an existing group G, + * then the depth of G is incremented and this new group G' has the + * depth of that group. For example, a group [C:D, depth 1] + [B:E] results in + * groups [B:E, depth 1] and [C:D, depth 2]. + * If the requested range is a subset of the range of an existing group G, + * then the depth of the new group G' becomes one greater than the depth of G. + * For example, a group [B:E, depth 1] + [C:D] results in groups [B:E, depth 1] + * and [C:D, depth 2]. + * If the requested range starts before and ends within, or starts within and + * ends after, the range of an existing group G, then the range of the existing + * group G becomes the union of the ranges, and the new group G' has + * depth one greater than the depth of G and range as the intersection of the + * ranges. For example, a group [B:D, depth 1] + [C:E] results in groups [B:E, + * depth 1] and [C:D, depth 2]. + */ +@interface GTLRSheets_AddDimensionGroupRequest : GTLRObject + +/** The range over which to create a group. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * The result of adding a group. + */ +@interface GTLRSheets_AddDimensionGroupResponse : GTLRObject + +/** All groups of a dimension after adding a group to that dimension. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionGroup *> *dimensionGroups; + +@end + + +/** + * Adds a filter view. + */ +@interface GTLRSheets_AddFilterViewRequest : GTLRObject + +/** + * The filter to add. The filterViewId + * field is optional; if one is not set, an id will be randomly generated. (It + * is an error to specify the ID of a filter that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_FilterView *filter; + +@end + + +/** + * The result of adding a filter view. + */ +@interface GTLRSheets_AddFilterViewResponse : GTLRObject + +/** The newly added filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_FilterView *filter; + +@end + + +/** + * Adds a named range to the spreadsheet. + */ +@interface GTLRSheets_AddNamedRangeRequest : GTLRObject + +/** + * The named range to add. The namedRangeId + * field is optional; if one is not set, an id will be randomly generated. (It + * is an error to specify the ID of a range that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_NamedRange *namedRange; + +@end + + +/** + * The result of adding a named range. + */ +@interface GTLRSheets_AddNamedRangeResponse : GTLRObject + +/** The named range to add. */ +@property(nonatomic, strong, nullable) GTLRSheets_NamedRange *namedRange; + +@end + + +/** + * Adds a new protected range. + */ +@interface GTLRSheets_AddProtectedRangeRequest : GTLRObject + +/** + * The protected range to be added. The + * protectedRangeId field is optional; if + * one is not set, an id will be randomly generated. (It is an error to + * specify the ID of a range that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_ProtectedRange *protectedRange; + +@end + + +/** + * The result of adding a new protected range. + */ +@interface GTLRSheets_AddProtectedRangeResponse : GTLRObject + +/** The newly added protected range. */ +@property(nonatomic, strong, nullable) GTLRSheets_ProtectedRange *protectedRange; + +@end + + +/** + * Adds a new sheet. + * When a sheet is added at a given index, + * all subsequent sheets' indexes are incremented. + * To add an object sheet, use AddChartRequest instead and specify + * EmbeddedObjectPosition.sheetId or + * EmbeddedObjectPosition.newSheet. + */ +@interface GTLRSheets_AddSheetRequest : GTLRObject + +/** + * The properties the new sheet should have. + * All properties are optional. + * The sheetId field is optional; if one is not + * set, an id will be randomly generated. (It is an error to specify the ID + * of a sheet that already exists.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_SheetProperties *properties; + +@end + + +/** + * The result of adding a sheet. + */ +@interface GTLRSheets_AddSheetResponse : GTLRObject + +/** The properties of the newly added sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_SheetProperties *properties; + +@end + + +/** + * Adds new cells after the last row with data in a sheet, + * inserting new rows into the sheet if necessary. + */ +@interface GTLRSheets_AppendCellsRequest : GTLRObject + +/** + * The fields of CellData that should be updated. + * At least one field must be specified. + * The root is the CellData; 'row.values.' should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The data to append. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_RowData *> *rows; + +/** + * The sheet ID to append the data to. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * Appends rows or columns to the end of a sheet. + */ +@interface GTLRSheets_AppendDimensionRequest : GTLRObject + +/** + * Whether rows or columns should be appended. + * + * Likely values: + * @arg @c kGTLRSheets_AppendDimensionRequest_Dimension_Columns Operates on + * the columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_AppendDimensionRequest_Dimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_AppendDimensionRequest_Dimension_Rows Operates on the + * rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *dimension; + +/** + * The number of rows or columns to append. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *length; + +/** + * The sheet to append rows or columns to. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * The response when updating a range of values in a spreadsheet. + */ +@interface GTLRSheets_AppendValuesResponse : GTLRObject + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The range (in A1 notation) of the table that values are being appended to + * (before the values were appended). + * Empty if no table was found. + */ +@property(nonatomic, copy, nullable) NSString *tableRange; + +/** Information about the updates that were applied. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateValuesResponse *updates; + +@end + + +/** + * Fills in more data based on existing data. + */ +@interface GTLRSheets_AutoFillRequest : GTLRObject + +/** + * The range to autofill. This will examine the range and detect + * the location that has data and automatically fill that data + * in to the rest of the range. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The source and destination areas to autofill. + * This explicitly lists the source of the autofill and where to + * extend that data. + */ +@property(nonatomic, strong, nullable) GTLRSheets_SourceAndDestination *sourceAndDestination; + +/** + * True if we should generate data with the "alternate" series. + * This differs based on the type and amount of source data. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *useAlternateSeries; + +@end + + +/** + * Automatically resizes one or more dimensions based on the contents + * of the cells in that dimension. + */ +@interface GTLRSheets_AutoResizeDimensionsRequest : GTLRObject + +/** The dimensions to automatically resize. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *dimensions; + +@end + + +/** + * A banded (alternating colors) range in a sheet. + */ +@interface GTLRSheets_BandedRange : GTLRObject + +/** + * The id of the banded range. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bandedRangeId; + +/** + * Properties for column bands. These properties are applied on a column- + * by-column basis throughout all the columns in the range. At least one of + * row_properties or column_properties must be specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_BandingProperties *columnProperties; + +/** The range over which these properties are applied. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * Properties for row bands. These properties are applied on a row-by-row + * basis throughout all the rows in the range. At least one of + * row_properties or column_properties must be specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_BandingProperties *rowProperties; + +@end + + +/** + * Properties referring a single dimension (either row or column). If both + * BandedRange.row_properties and BandedRange.column_properties are + * set, the fill colors are applied to cells according to the following rules: + * * header_color and footer_color take priority over band colors. + * * first_band_color takes priority over second_band_color. + * * row_properties takes priority over column_properties. + * For example, the first row color takes priority over the first column + * color, but the first column color takes priority over the second row color. + * Similarly, the row header takes priority over the column header in the + * top left cell, but the column header takes priority over the first row + * color if the row header is not set. + */ +@interface GTLRSheets_BandingProperties : GTLRObject + +/** The first color that is alternating. (Required) */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *firstBandColor; + +/** + * The color of the last row or column. If this field is not set, the last + * row or column will be filled with either first_band_color or + * second_band_color, depending on the color of the previous row or + * column. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *footerColor; + +/** + * The color of the first row or column. If this field is set, the first + * row or column will be filled with this color and the colors will + * alternate between first_band_color and second_band_color starting + * from the second row or column. Otherwise, the first row or column will be + * filled with first_band_color and the colors will proceed to alternate + * as they normally would. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *headerColor; + +/** The second color that is alternating. (Required) */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *secondBandColor; + +@end + + +/** + * An axis of the chart. + * A chart may not have more than one axis per + * axis position. + */ +@interface GTLRSheets_BasicChartAxis : GTLRObject + +/** + * The format of the title. + * Only valid if the axis is not associated with the domain. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *format; + +/** + * The position of this axis. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartAxis_Position_BasicChartAxisPositionUnspecified + * Default value, do not use. (Value: + * "BASIC_CHART_AXIS_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartAxis_Position_BottomAxis The axis rendered + * at the bottom of a chart. + * For most charts, this is the standard major axis. + * For bar charts, this is a minor axis. (Value: "BOTTOM_AXIS") + * @arg @c kGTLRSheets_BasicChartAxis_Position_LeftAxis The axis rendered at + * the left of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is the standard major axis. (Value: "LEFT_AXIS") + * @arg @c kGTLRSheets_BasicChartAxis_Position_RightAxis The axis rendered at + * the right of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is an unusual major axis. (Value: "RIGHT_AXIS") + */ +@property(nonatomic, copy, nullable) NSString *position; + +/** + * The title of this axis. If set, this overrides any title inferred + * from headers of the data. + */ +@property(nonatomic, copy, nullable) NSString *title; + +/** The axis title text position. */ +@property(nonatomic, strong, nullable) GTLRSheets_TextPosition *titleTextPosition; + +@end + + +/** + * The domain of a chart. + * For example, if charting stock prices over time, this would be the date. + */ +@interface GTLRSheets_BasicChartDomain : GTLRObject + +/** + * The data of the domain. For example, if charting stock prices over time, + * this is the data representing the dates. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *domain; + +/** + * True to reverse the order of the domain values (horizontal axis). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *reversed; + +@end + + +/** + * A single series of data in a chart. + * For example, if charting stock prices over time, multiple series may exist, + * one for the "Open Price", "High Price", "Low Price" and "Close Price". + */ +@interface GTLRSheets_BasicChartSeries : GTLRObject + +/** + * The color for elements (i.e. bars, lines, points) associated with this + * series. If empty, a default color is used. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *color; + +/** + * The line style of this series. Valid only if the + * chartType is AREA, + * LINE, or SCATTER. + * COMBO charts are also supported if the + * series chart type is + * AREA or LINE. + */ +@property(nonatomic, strong, nullable) GTLRSheets_LineStyle *lineStyle; + +/** The data being visualized in this chart series. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *series; + +/** + * The minor axis that will specify the range of values for this series. + * For example, if charting stocks over time, the "Volume" series + * may want to be pinned to the right with the prices pinned to the left, + * because the scale of trading volume is different than the scale of + * prices. + * It is an error to specify an axis that isn't a valid minor axis + * for the chart's type. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSeries_TargetAxis_BasicChartAxisPositionUnspecified + * Default value, do not use. (Value: + * "BASIC_CHART_AXIS_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSeries_TargetAxis_BottomAxis The axis + * rendered at the bottom of a chart. + * For most charts, this is the standard major axis. + * For bar charts, this is a minor axis. (Value: "BOTTOM_AXIS") + * @arg @c kGTLRSheets_BasicChartSeries_TargetAxis_LeftAxis The axis rendered + * at the left of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is the standard major axis. (Value: "LEFT_AXIS") + * @arg @c kGTLRSheets_BasicChartSeries_TargetAxis_RightAxis The axis + * rendered at the right of a chart. + * For most charts, this is a minor axis. + * For bar charts, this is an unusual major axis. (Value: "RIGHT_AXIS") + */ +@property(nonatomic, copy, nullable) NSString *targetAxis; + +/** + * The type of this series. Valid only if the + * chartType is + * COMBO. + * Different types will change the way the series is visualized. + * Only LINE, AREA, + * and COLUMN are supported. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSeries_Type_Area An + * <a href="/chart/interactive/docs/gallery/areachart">area chart</a>. + * (Value: "AREA") + * @arg @c kGTLRSheets_BasicChartSeries_Type_Bar A + * <a href="/chart/interactive/docs/gallery/barchart">bar chart</a>. + * (Value: "BAR") + * @arg @c kGTLRSheets_BasicChartSeries_Type_BasicChartTypeUnspecified + * Default value, do not use. (Value: "BASIC_CHART_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSeries_Type_Column A + * <a href="/chart/interactive/docs/gallery/columnchart">column + * chart</a>. (Value: "COLUMN") + * @arg @c kGTLRSheets_BasicChartSeries_Type_Combo A + * <a href="/chart/interactive/docs/gallery/combochart">combo chart</a>. + * (Value: "COMBO") + * @arg @c kGTLRSheets_BasicChartSeries_Type_Line A + * <a href="/chart/interactive/docs/gallery/linechart">line chart</a>. + * (Value: "LINE") + * @arg @c kGTLRSheets_BasicChartSeries_Type_Scatter A + * <a href="/chart/interactive/docs/gallery/scatterchart">scatter + * chart</a>. (Value: "SCATTER") + * @arg @c kGTLRSheets_BasicChartSeries_Type_SteppedArea A + * <a href="/chart/interactive/docs/gallery/steppedareachart">stepped + * area + * chart</a>. (Value: "STEPPED_AREA") + */ +@property(nonatomic, copy, nullable) NSString *type; + +@end + + +/** + * The specification for a basic chart. See BasicChartType for the list + * of charts this supports. + */ +@interface GTLRSheets_BasicChartSpec : GTLRObject + +/** The axis on the chart. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_BasicChartAxis *> *axis; + +/** + * The type of the chart. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Area An + * <a href="/chart/interactive/docs/gallery/areachart">area chart</a>. + * (Value: "AREA") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Bar A + * <a href="/chart/interactive/docs/gallery/barchart">bar chart</a>. + * (Value: "BAR") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_BasicChartTypeUnspecified + * Default value, do not use. (Value: "BASIC_CHART_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Column A + * <a href="/chart/interactive/docs/gallery/columnchart">column + * chart</a>. (Value: "COLUMN") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Combo A + * <a href="/chart/interactive/docs/gallery/combochart">combo chart</a>. + * (Value: "COMBO") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Line A + * <a href="/chart/interactive/docs/gallery/linechart">line chart</a>. + * (Value: "LINE") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_Scatter A + * <a href="/chart/interactive/docs/gallery/scatterchart">scatter + * chart</a>. (Value: "SCATTER") + * @arg @c kGTLRSheets_BasicChartSpec_ChartType_SteppedArea A + * <a href="/chart/interactive/docs/gallery/steppedareachart">stepped + * area + * chart</a>. (Value: "STEPPED_AREA") + */ +@property(nonatomic, copy, nullable) NSString *chartType; + +/** + * The behavior of tooltips and data highlighting when hovering on data and + * chart area. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSpec_CompareMode_BasicChartCompareModeUnspecified + * Default value, do not use. (Value: + * "BASIC_CHART_COMPARE_MODE_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSpec_CompareMode_Category All data elements + * with the same category (e.g., domain value) are + * highlighted and shown in the tooltip. (Value: "CATEGORY") + * @arg @c kGTLRSheets_BasicChartSpec_CompareMode_Datum Only the focused data + * element is highlighted and shown in the tooltip. (Value: "DATUM") + */ +@property(nonatomic, copy, nullable) NSString *compareMode; + +/** + * The domain of data this is charting. + * Only a single domain is supported. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_BasicChartDomain *> *domains; + +/** + * The number of rows or columns in the data that are "headers". + * If not set, Google Sheets will guess how many rows are headers based + * on the data. + * (Note that BasicChartAxis.title may override the axis title + * inferred from the header values.) + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *headerCount; + +/** + * If some values in a series are missing, gaps may appear in the chart (e.g, + * segments of lines in a line chart will be missing). To eliminate these + * gaps set this to true. + * Applies to Line, Area, and Combo charts. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *interpolateNulls; + +/** + * The position of the chart legend. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_BasicChartLegendPositionUnspecified + * Default value, do not use. (Value: + * "BASIC_CHART_LEGEND_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_BottomLegend The legend + * is rendered on the bottom of the chart. (Value: "BOTTOM_LEGEND") + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_LeftLegend The legend is + * rendered on the left of the chart. (Value: "LEFT_LEGEND") + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_NoLegend No legend is + * rendered. (Value: "NO_LEGEND") + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_RightLegend The legend + * is rendered on the right of the chart. (Value: "RIGHT_LEGEND") + * @arg @c kGTLRSheets_BasicChartSpec_LegendPosition_TopLegend The legend is + * rendered on the top of the chart. (Value: "TOP_LEGEND") + */ +@property(nonatomic, copy, nullable) NSString *legendPosition; + +/** + * Gets whether all lines should be rendered smooth or straight by default. + * Applies to Line charts. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *lineSmoothing; + +/** The data this chart is visualizing. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_BasicChartSeries *> *series; + +/** + * The stacked type for charts that support vertical stacking. + * Applies to Area, Bar, Column, Combo, and Stepped Area charts. + * + * Likely values: + * @arg @c kGTLRSheets_BasicChartSpec_StackedType_BasicChartStackedTypeUnspecified + * Default value, do not use. (Value: + * "BASIC_CHART_STACKED_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_BasicChartSpec_StackedType_NotStacked Series are not + * stacked. (Value: "NOT_STACKED") + * @arg @c kGTLRSheets_BasicChartSpec_StackedType_PercentStacked Vertical + * stacks are stretched to reach the top of the chart, with + * values laid out as percentages of each other. (Value: + * "PERCENT_STACKED") + * @arg @c kGTLRSheets_BasicChartSpec_StackedType_Stacked Series values are + * stacked, each value is rendered vertically beginning + * from the top of the value below it. (Value: "STACKED") + */ +@property(nonatomic, copy, nullable) NSString *stackedType; + +/** + * True to make the chart 3D. + * Applies to Bar and Column charts. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *threeDimensional; + +@end + + +/** + * The default filter associated with a sheet. + */ +@interface GTLRSheets_BasicFilter : GTLRObject + +/** + * The criteria for showing/hiding values per column. + * The map's key is the column index, and the value is the criteria for + * that column. + */ +@property(nonatomic, strong, nullable) GTLRSheets_BasicFilter_Criteria *criteria; + +/** The range the filter covers. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The sort order per column. Later specifications are used when values + * are equal in the earlier specifications. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_SortSpec *> *sortSpecs; + +@end + + +/** + * The criteria for showing/hiding values per column. + * The map's key is the column index, and the value is the criteria for + * that column. + * + * @note This class is documented as having more properties of + * GTLRSheets_FilterCriteria. Use @c -additionalJSONKeys and @c + * -additionalPropertyForName: to get the list of properties and then + * fetch them; or @c -additionalProperties to fetch them all at once. + */ +@interface GTLRSheets_BasicFilter_Criteria : GTLRObject +@end + + +/** + * The request for clearing more than one range selected by a + * DataFilter in a spreadsheet. + */ +@interface GTLRSheets_BatchClearValuesByDataFilterRequest : GTLRObject + +/** The DataFilters used to determine which ranges to clear. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +@end + + +/** + * The response when clearing a range of values selected with + * DataFilters in a spreadsheet. + */ +@interface GTLRSheets_BatchClearValuesByDataFilterResponse : GTLRObject + +/** + * The ranges that were cleared, in A1 notation. + * (If the requests were for an unbounded range or a ranger larger + * than the bounds of the sheet, this will be the actual ranges + * that were cleared, bounded to the sheet's limits.) + */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *clearedRanges; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +@end + + +/** + * The request for clearing more than one range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchClearValuesRequest : GTLRObject + +/** The ranges to clear, in A1 notation. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *ranges; + +@end + + +/** + * The response when clearing a range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchClearValuesResponse : GTLRObject + +/** + * The ranges that were cleared, in A1 notation. + * (If the requests were for an unbounded range or a ranger larger + * than the bounds of the sheet, this will be the actual ranges + * that were cleared, bounded to the sheet's limits.) + */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *clearedRanges; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +@end + + +/** + * The request for retrieving a range of values in a spreadsheet selected by a + * set of DataFilters. + */ +@interface GTLRSheets_BatchGetValuesByDataFilterRequest : GTLRObject + +/** + * The data filters used to match the ranges of values to retrieve. Ranges + * that match any of the specified data filters will be included in the + * response. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +/** + * How dates, times, and durations should be represented in the output. + * This is ignored if value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is [DateTimeRenderOption.SERIAL_NUMBER]. + * + * Likely values: + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_FormattedString + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). (Value: "FORMATTED_STRING") + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_SerialNumber + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. (Value: "SERIAL_NUMBER") + */ +@property(nonatomic, copy, nullable) NSString *dateTimeRenderOption; + +/** + * The major dimension that results should use. + * For example, if the spreadsheet data is: `A1=1,B1=2,A2=3,B2=4`, + * then a request that selects that range and sets `majorDimension=ROWS` will + * return `[[1,2],[3,4]]`, + * whereas a request that sets `majorDimension=COLUMNS` will return + * `[[1,3],[2,4]]`. + * + * Likely values: + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Columns + * Operates on the columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Rows + * Operates on the rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *majorDimension; + +/** + * How values should be represented in the output. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_FormattedValue + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return `"$1.23"`. (Value: "FORMATTED_VALUE") + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_Formula + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then A2 would return `"=A1"`. (Value: "FORMULA") + * @arg @c kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_UnformattedValue + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return the number `1.23`. (Value: "UNFORMATTED_VALUE") + */ +@property(nonatomic, copy, nullable) NSString *valueRenderOption; + +@end + + +/** + * The response when retrieving more than one range of values in a spreadsheet + * selected by DataFilters. + */ +@interface GTLRSheets_BatchGetValuesByDataFilterResponse : GTLRObject + +/** The ID of the spreadsheet the data was retrieved from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** The requested values with the list of data filters that matched them. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_MatchedValueRange *> *valueRanges; + +@end + + +/** + * The response when retrieving more than one range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchGetValuesResponse : GTLRObject + +/** The ID of the spreadsheet the data was retrieved from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The requested values. The order of the ValueRanges is the same as the + * order of the requested ranges. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ValueRange *> *valueRanges; + +@end + + +/** + * The request for updating any aspect of a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateSpreadsheetRequest : GTLRObject + +/** + * Determines if the update response should include the spreadsheet + * resource. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *includeSpreadsheetInResponse; + +/** + * A list of updates to apply to the spreadsheet. + * Requests will be applied in the order they are specified. + * If any request is not valid, no requests will be applied. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_Request *> *requests; + +/** + * True if grid data should be returned. Meaningful only if + * if include_spreadsheet_in_response is 'true'. + * This parameter is ignored if a field mask was set in the request. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *responseIncludeGridData; + +/** + * Limits the ranges included in the response spreadsheet. + * Meaningful only if include_spreadsheet_response is 'true'. + */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *responseRanges; + +@end + + +/** + * The reply for batch updating a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateSpreadsheetResponse : GTLRObject + +/** + * The reply of the updates. This maps 1:1 with the updates, although + * replies to some requests may be empty. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_Response *> *replies; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The spreadsheet after updates were applied. This is only set if + * [BatchUpdateSpreadsheetRequest.include_spreadsheet_in_response] is `true`. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Spreadsheet *updatedSpreadsheet; + +@end + + +/** + * The request for updating more than one range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateValuesByDataFilterRequest : GTLRObject + +/** + * The new values to apply to the spreadsheet. If more than one range is + * matched by the specified DataFilter the specified values will be + * applied to all of those ranges. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilterValueRange *> *data; + +/** + * Determines if the update response should include the values + * of the cells that were updated. By default, responses + * do not include the updated values. The `updatedData` field within + * each of the BatchUpdateValuesResponse.responses will contain + * the updated values. If the range to write was larger than than the range + * actually written, the response will include all values in the requested + * range (excluding trailing empty rows and columns). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *includeValuesInResponse; + +/** + * Determines how dates, times, and durations in the response should be + * rendered. This is ignored if response_value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is + * DateTimeRenderOption.SERIAL_NUMBER. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_FormattedString + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). (Value: "FORMATTED_STRING") + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_SerialNumber + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. (Value: "SERIAL_NUMBER") + */ +@property(nonatomic, copy, nullable) NSString *responseDateTimeRenderOption; + +/** + * Determines how values in the response should be rendered. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_FormattedValue + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return `"$1.23"`. (Value: "FORMATTED_VALUE") + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_Formula + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then A2 would return `"=A1"`. (Value: "FORMULA") + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_UnformattedValue + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return the number `1.23`. (Value: "UNFORMATTED_VALUE") + */ +@property(nonatomic, copy, nullable) NSString *responseValueRenderOption; + +/** + * How the input data should be interpreted. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_InputValueOptionUnspecified + * Default input value. This value must not be used. (Value: + * "INPUT_VALUE_OPTION_UNSPECIFIED") + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_Raw + * The values the user has entered will not be parsed and will be stored + * as-is. (Value: "RAW") + * @arg @c kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_UserEntered + * The values will be parsed as if the user typed them into the UI. + * Numbers will stay as numbers, but strings may be converted to numbers, + * dates, etc. following the same rules that are applied when entering + * text into a cell via the Google Sheets UI. (Value: "USER_ENTERED") + */ +@property(nonatomic, copy, nullable) NSString *valueInputOption; + +@end + + +/** + * The response when updating a range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateValuesByDataFilterResponse : GTLRObject + +/** The response for each range updated. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_UpdateValuesByDataFilterResponse *> *responses; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The total number of cells updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedCells; + +/** + * The total number of columns where at least one cell in the column was + * updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedColumns; + +/** + * The total number of rows where at least one cell in the row was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedRows; + +/** + * The total number of sheets where at least one cell in the sheet was + * updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedSheets; + +@end + + +/** + * The request for updating more than one range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateValuesRequest : GTLRObject + +/** The new values to apply to the spreadsheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ValueRange *> *data; + +/** + * Determines if the update response should include the values + * of the cells that were updated. By default, responses + * do not include the updated values. The `updatedData` field within + * each of the BatchUpdateValuesResponse.responses will contain + * the updated values. If the range to write was larger than than the range + * actually written, the response will include all values in the requested + * range (excluding trailing empty rows and columns). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *includeValuesInResponse; + +/** + * Determines how dates, times, and durations in the response should be + * rendered. This is ignored if response_value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is + * DateTimeRenderOption.SERIAL_NUMBER. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_FormattedString + * Instructs date, time, datetime, and duration fields to be output + * as strings in their given number format (which is dependent + * on the spreadsheet locale). (Value: "FORMATTED_STRING") + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_SerialNumber + * Instructs date, time, datetime, and duration fields to be output + * as doubles in "serial number" format, as popularized by Lotus 1-2-3. + * The whole number portion of the value (left of the decimal) counts + * the days since December 30th 1899. The fractional portion (right of + * the decimal) counts the time as a fraction of the day. For example, + * January 1st 1900 at noon would be 2.5, 2 because it's 2 days after + * December 30st 1899, and .5 because noon is half a day. February 1st + * 1900 at 3pm would be 33.625. This correctly treats the year 1900 as + * not a leap year. (Value: "SERIAL_NUMBER") + */ +@property(nonatomic, copy, nullable) NSString *responseDateTimeRenderOption; + +/** + * Determines how values in the response should be rendered. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_FormattedValue + * Values will be calculated & formatted in the reply according to the + * cell's formatting. Formatting is based on the spreadsheet's locale, + * not the requesting user's locale. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return `"$1.23"`. (Value: "FORMATTED_VALUE") + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_Formula + * Values will not be calculated. The reply will include the formulas. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then A2 would return `"=A1"`. (Value: "FORMULA") + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_UnformattedValue + * Values will be calculated, but not formatted in the reply. + * For example, if `A1` is `1.23` and `A2` is `=A1` and formatted as + * currency, + * then `A2` would return the number `1.23`. (Value: "UNFORMATTED_VALUE") + */ +@property(nonatomic, copy, nullable) NSString *responseValueRenderOption; + +/** + * How the input data should be interpreted. + * + * Likely values: + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_InputValueOptionUnspecified + * Default input value. This value must not be used. (Value: + * "INPUT_VALUE_OPTION_UNSPECIFIED") + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_Raw The + * values the user has entered will not be parsed and will be stored + * as-is. (Value: "RAW") + * @arg @c kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_UserEntered + * The values will be parsed as if the user typed them into the UI. + * Numbers will stay as numbers, but strings may be converted to numbers, + * dates, etc. following the same rules that are applied when entering + * text into a cell via the Google Sheets UI. (Value: "USER_ENTERED") + */ +@property(nonatomic, copy, nullable) NSString *valueInputOption; + +@end + + +/** + * The response when updating a range of values in a spreadsheet. + */ +@interface GTLRSheets_BatchUpdateValuesResponse : GTLRObject + +/** + * One UpdateValuesResponse per requested range, in the same order as + * the requests appeared. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_UpdateValuesResponse *> *responses; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The total number of cells updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedCells; + +/** + * The total number of columns where at least one cell in the column was + * updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedColumns; + +/** + * The total number of rows where at least one cell in the row was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedRows; + +/** + * The total number of sheets where at least one cell in the sheet was + * updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *totalUpdatedSheets; + +@end + + +/** + * A condition that can evaluate to true or false. + * BooleanConditions are used by conditional formatting, + * data validation, and the criteria in filters. + */ +@interface GTLRSheets_BooleanCondition : GTLRObject + +/** + * The type of condition. + * + * Likely values: + * @arg @c kGTLRSheets_BooleanCondition_Type_Blank The cell's value must be + * empty. + * Supported by conditional formatting and filters. + * Requires no ConditionValues. (Value: "BLANK") + * @arg @c kGTLRSheets_BooleanCondition_Type_Boolean The cell's value must be + * TRUE/FALSE or in the list of condition values. + * Supported by data validation. + * Renders as a cell checkbox. + * Supports zero, one or two ConditionValues. No + * values indicates the cell must be TRUE or FALSE, where TRUE renders as + * checked and FALSE renders as unchecked. One value indicates the cell + * will render as checked when it contains that value and unchecked when + * it + * is blank. Two values indicate that the cell will render as checked + * when + * it contains the first value and unchecked when it contains the second + * value. For example, ["Yes","No"] indicates that the cell will render a + * checked box when it has the value "Yes" and an unchecked box when it + * has + * the value "No". (Value: "BOOLEAN") + * @arg @c kGTLRSheets_BooleanCondition_Type_ConditionTypeUnspecified The + * default value, do not use. (Value: "CONDITION_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_BooleanCondition_Type_CustomFormula The condition's + * formula must evaluate to true. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "CUSTOM_FORMULA") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateAfter The cell's value must + * be after the date of the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue + * that may be a relative date. (Value: "DATE_AFTER") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateBefore The cell's value must + * be before the date of the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue + * that may be a relative date. (Value: "DATE_BEFORE") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateBetween The cell's value + * must be between the dates of the two condition values. + * Supported by data validation. + * Requires exactly two ConditionValues. (Value: "DATE_BETWEEN") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateEq The cell's value must be + * the same date as the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "DATE_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateIsValid The cell's value + * must be a date. + * Supported by data validation. + * Requires no ConditionValues. (Value: "DATE_IS_VALID") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateNotBetween The cell's value + * must be outside the dates of the two condition values. + * Supported by data validation. + * Requires exactly two ConditionValues. (Value: "DATE_NOT_BETWEEN") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateOnOrAfter The cell's value + * must be on or after the date of the condition's value. + * Supported by data validation. + * Requires a single ConditionValue + * that may be a relative date. (Value: "DATE_ON_OR_AFTER") + * @arg @c kGTLRSheets_BooleanCondition_Type_DateOnOrBefore The cell's value + * must be on or before the date of the condition's value. + * Supported by data validation. + * Requires a single ConditionValue + * that may be a relative date. (Value: "DATE_ON_OR_BEFORE") + * @arg @c kGTLRSheets_BooleanCondition_Type_NotBlank The cell's value must + * not be empty. + * Supported by conditional formatting and filters. + * Requires no ConditionValues. (Value: "NOT_BLANK") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberBetween The cell's value + * must be between the two condition values. + * Supported by data validation, conditional formatting and filters. + * Requires exactly two ConditionValues. (Value: "NUMBER_BETWEEN") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberEq The cell's value must + * be equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberGreater The cell's value + * must be greater than the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_GREATER") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberGreaterThanEq The cell's + * value must be greater than or equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_GREATER_THAN_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberLess The cell's value must + * be less than the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_LESS") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberLessThanEq The cell's + * value must be less than or equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_LESS_THAN_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberNotBetween The cell's + * value must not be between the two condition values. + * Supported by data validation, conditional formatting and filters. + * Requires exactly two ConditionValues. (Value: "NUMBER_NOT_BETWEEN") + * @arg @c kGTLRSheets_BooleanCondition_Type_NumberNotEq The cell's value + * must be not equal to the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "NUMBER_NOT_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_OneOfList The cell's value must + * be in the list of condition values. + * Supported by data validation. + * Supports any number of condition values, + * one per item in the list. + * Formulas are not supported in the values. (Value: "ONE_OF_LIST") + * @arg @c kGTLRSheets_BooleanCondition_Type_OneOfRange The cell's value must + * be listed in the grid in condition value's range. + * Supported by data validation. + * Requires a single ConditionValue, + * and the value must be a valid range in A1 notation. (Value: + * "ONE_OF_RANGE") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextContains The cell's value + * must contain the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "TEXT_CONTAINS") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextEndsWith The cell's value + * must end with the condition's value. + * Supported by conditional formatting and filters. + * Requires a single ConditionValue. (Value: "TEXT_ENDS_WITH") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextEq The cell's value must be + * exactly the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "TEXT_EQ") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextIsEmail The cell's value + * must be a valid email address. + * Supported by data validation. + * Requires no ConditionValues. (Value: "TEXT_IS_EMAIL") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextIsUrl The cell's value must + * be a valid URL. + * Supported by data validation. + * Requires no ConditionValues. (Value: "TEXT_IS_URL") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextNotContains The cell's value + * must not contain the condition's value. + * Supported by data validation, conditional formatting and filters. + * Requires a single ConditionValue. (Value: "TEXT_NOT_CONTAINS") + * @arg @c kGTLRSheets_BooleanCondition_Type_TextStartsWith The cell's value + * must start with the condition's value. + * Supported by conditional formatting and filters. + * Requires a single ConditionValue. (Value: "TEXT_STARTS_WITH") + */ +@property(nonatomic, copy, nullable) NSString *type; + +/** + * The values of the condition. The number of supported values depends + * on the condition type. Some support zero values, + * others one or two values, + * and ConditionType.ONE_OF_LIST supports an arbitrary number of values. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ConditionValue *> *values; + +@end + + +/** + * A rule that may or may not match, depending on the condition. + */ +@interface GTLRSheets_BooleanRule : GTLRObject + +/** + * The condition of the rule. If the condition evaluates to true, + * the format is applied. + */ +@property(nonatomic, strong, nullable) GTLRSheets_BooleanCondition *condition; + +/** + * The format to apply. + * Conditional formatting can only apply a subset of formatting: + * bold, italic, + * strikethrough, + * foreground color & + * background color. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CellFormat *format; + +@end + + +/** + * A border along a cell. + */ +@interface GTLRSheets_Border : GTLRObject + +/** The color of the border. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *color; + +/** + * The style of the border. + * + * Likely values: + * @arg @c kGTLRSheets_Border_Style_Dashed The border is dashed. (Value: + * "DASHED") + * @arg @c kGTLRSheets_Border_Style_Dotted The border is dotted. (Value: + * "DOTTED") + * @arg @c kGTLRSheets_Border_Style_Double The border is two solid lines. + * (Value: "DOUBLE") + * @arg @c kGTLRSheets_Border_Style_None No border. + * Used only when updating a border in order to erase it. (Value: "NONE") + * @arg @c kGTLRSheets_Border_Style_Solid The border is a thin solid line. + * (Value: "SOLID") + * @arg @c kGTLRSheets_Border_Style_SolidMedium The border is a medium solid + * line. (Value: "SOLID_MEDIUM") + * @arg @c kGTLRSheets_Border_Style_SolidThick The border is a thick solid + * line. (Value: "SOLID_THICK") + * @arg @c kGTLRSheets_Border_Style_StyleUnspecified The style is not + * specified. Do not use this. (Value: "STYLE_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *style; + +/** + * The width of the border, in pixels. + * Deprecated; the width is determined by the "style" field. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *width; + +@end + + +/** + * The borders of the cell. + */ +@interface GTLRSheets_Borders : GTLRObject + +/** The bottom border of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *bottom; + +/** The left border of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *left; + +/** The right border of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *right; + +/** The top border of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *top; + +@end + + +/** + * A <a href="/chart/interactive/docs/gallery/bubblechart">bubble chart</a>. + */ +@interface GTLRSheets_BubbleChartSpec : GTLRObject + +/** The bubble border color. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *bubbleBorderColor; + +/** The data containing the bubble labels. These do not need to be unique. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *bubbleLabels; + +/** + * The max radius size of the bubbles, in pixels. + * If specified, the field must be a positive value. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bubbleMaxRadiusSize; + +/** + * The minimum radius size of the bubbles, in pixels. + * If specific, the field must be a positive value. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bubbleMinRadiusSize; + +/** + * The opacity of the bubbles between 0 and 1.0. + * 0 is fully transparent and 1 is fully opaque. + * + * Uses NSNumber of floatValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bubbleOpacity; + +/** + * The data contianing the bubble sizes. Bubble sizes are used to draw + * the bubbles at different sizes relative to each other. + * If specified, group_ids must also be specified. This field is + * optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *bubbleSizes; + +/** + * The format of the text inside the bubbles. + * Underline and Strikethrough are not supported. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *bubbleTextStyle; + +/** + * The data containing the bubble x-values. These values locate the bubbles + * in the chart horizontally. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *domain; + +/** + * The data containing the bubble group IDs. All bubbles with the same group + * ID are drawn in the same color. If bubble_sizes is specified then + * this field must also be specified but may contain blank values. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *groupIds; + +/** + * Where the legend of the chart should be drawn. + * + * Likely values: + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_BottomLegend The legend + * is rendered on the bottom of the chart. (Value: "BOTTOM_LEGEND") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_BubbleChartLegendPositionUnspecified + * Default value, do not use. (Value: + * "BUBBLE_CHART_LEGEND_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_InsideLegend The legend + * is rendered inside the chart area. (Value: "INSIDE_LEGEND") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_LeftLegend The legend + * is rendered on the left of the chart. (Value: "LEFT_LEGEND") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_NoLegend No legend is + * rendered. (Value: "NO_LEGEND") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_RightLegend The legend + * is rendered on the right of the chart. (Value: "RIGHT_LEGEND") + * @arg @c kGTLRSheets_BubbleChartSpec_LegendPosition_TopLegend The legend is + * rendered on the top of the chart. (Value: "TOP_LEGEND") + */ +@property(nonatomic, copy, nullable) NSString *legendPosition; + +/** + * The data contianing the bubble y-values. These values locate the bubbles + * in the chart vertically. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *series; + +@end + + +/** + * A <a href="/chart/interactive/docs/gallery/candlestickchart">candlestick + * chart</a>. + */ +@interface GTLRSheets_CandlestickChartSpec : GTLRObject + +/** + * The Candlestick chart data. + * Only one CandlestickData is supported. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_CandlestickData *> *data; + +/** + * The domain data (horizontal axis) for the candlestick chart. String data + * will be treated as discrete labels, other data will be treated as + * continuous values. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickDomain *domain; + +@end + + +/** + * The Candlestick chart data, each containing the low, open, close, and high + * values for a series. + */ +@interface GTLRSheets_CandlestickData : GTLRObject + +/** + * The range data (vertical axis) for the close/final value for each candle. + * This is the top of the candle body. If greater than the open value the + * candle will be filled. Otherwise the candle will be hollow. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickSeries *closeSeries; + +/** + * The range data (vertical axis) for the high/maximum value for each + * candle. This is the top of the candle's center line. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickSeries *highSeries; + +/** + * The range data (vertical axis) for the low/minimum value for each candle. + * This is the bottom of the candle's center line. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickSeries *lowSeries; + +/** + * The range data (vertical axis) for the open/initial value for each + * candle. This is the bottom of the candle body. If less than the close + * value the candle will be filled. Otherwise the candle will be hollow. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickSeries *openSeries; + +@end + + +/** + * The domain of a CandlestickChart. + */ +@interface GTLRSheets_CandlestickDomain : GTLRObject + +/** The data of the CandlestickDomain. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *data; + +/** + * True to reverse the order of the domain values (horizontal axis). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *reversed; + +@end + + +/** + * The series of a CandlestickData. + */ +@interface GTLRSheets_CandlestickSeries : GTLRObject + +/** The data of the CandlestickSeries. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *data; + +@end + + +/** + * Data about a specific cell. + */ +@interface GTLRSheets_CellData : GTLRObject + +/** + * A data validation rule on the cell, if any. + * When writing, the new data validation rule will overwrite any prior rule. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DataValidationRule *dataValidation; + +/** + * The effective format being used by the cell. + * This includes the results of applying any conditional formatting and, + * if the cell contains a formula, the computed number format. + * If the effective format is the default format, effective format will + * not be written. + * This field is read-only. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CellFormat *effectiveFormat; + +/** + * The effective value of the cell. For cells with formulas, this is + * the calculated value. For cells with literals, this is + * the same as the user_entered_value. + * This field is read-only. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ExtendedValue *effectiveValue; + +/** + * The formatted value of the cell. + * This is the value as it's shown to the user. + * This field is read-only. + */ +@property(nonatomic, copy, nullable) NSString *formattedValue; + +/** + * A hyperlink this cell points to, if any. + * This field is read-only. (To set it, use a `=HYPERLINK` formula + * in the userEnteredValue.formulaValue + * field.) + */ +@property(nonatomic, copy, nullable) NSString *hyperlink; + +/** Any note on the cell. */ +@property(nonatomic, copy, nullable) NSString *note; + +/** + * A pivot table anchored at this cell. The size of pivot table itself + * is computed dynamically based on its data, grouping, filters, values, + * etc. Only the top-left cell of the pivot table contains the pivot table + * definition. The other cells will contain the calculated values of the + * results of the pivot in their effective_value fields. + */ +@property(nonatomic, strong, nullable) GTLRSheets_PivotTable *pivotTable; + +/** + * Runs of rich text applied to subsections of the cell. Runs are only valid + * on user entered strings, not formulas, bools, or numbers. + * Runs start at specific indexes in the text and continue until the next + * run. Properties of a run will continue unless explicitly changed + * in a subsequent run (and properties of the first run will continue + * the properties of the cell unless explicitly changed). + * When writing, the new runs will overwrite any prior runs. When writing a + * new user_entered_value, previous runs are erased. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_TextFormatRun *> *textFormatRuns; + +/** + * The format the user entered for the cell. + * When writing, the new format will be merged with the existing format. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CellFormat *userEnteredFormat; + +/** + * The value the user entered in the cell. e.g, `1234`, `'Hello'`, or `=NOW()` + * Note: Dates, Times and DateTimes are represented as doubles in + * serial number format. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ExtendedValue *userEnteredValue; + +@end + + +/** + * The format of a cell. + */ +@interface GTLRSheets_CellFormat : GTLRObject + +/** The background color of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *backgroundColor; + +/** The borders of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Borders *borders; + +/** + * The horizontal alignment of the value in the cell. + * + * Likely values: + * @arg @c kGTLRSheets_CellFormat_HorizontalAlignment_Center The text is + * explicitly aligned to the center of the cell. (Value: "CENTER") + * @arg @c kGTLRSheets_CellFormat_HorizontalAlignment_HorizontalAlignUnspecified + * The horizontal alignment is not specified. Do not use this. (Value: + * "HORIZONTAL_ALIGN_UNSPECIFIED") + * @arg @c kGTLRSheets_CellFormat_HorizontalAlignment_Left The text is + * explicitly aligned to the left of the cell. (Value: "LEFT") + * @arg @c kGTLRSheets_CellFormat_HorizontalAlignment_Right The text is + * explicitly aligned to the right of the cell. (Value: "RIGHT") + */ +@property(nonatomic, copy, nullable) NSString *horizontalAlignment; + +/** + * How a hyperlink, if it exists, should be displayed in the cell. + * + * Likely values: + * @arg @c kGTLRSheets_CellFormat_HyperlinkDisplayType_HyperlinkDisplayTypeUnspecified + * The default value: the hyperlink is rendered. Do not use this. (Value: + * "HYPERLINK_DISPLAY_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_CellFormat_HyperlinkDisplayType_Linked A hyperlink + * should be explicitly rendered. (Value: "LINKED") + * @arg @c kGTLRSheets_CellFormat_HyperlinkDisplayType_PlainText A hyperlink + * should not be rendered. (Value: "PLAIN_TEXT") + */ +@property(nonatomic, copy, nullable) NSString *hyperlinkDisplayType; + +/** + * A format describing how number values should be represented to the user. + */ +@property(nonatomic, strong, nullable) GTLRSheets_NumberFormat *numberFormat; + +/** The padding of the cell. */ +@property(nonatomic, strong, nullable) GTLRSheets_Padding *padding; + +/** + * The direction of the text in the cell. + * + * Likely values: + * @arg @c kGTLRSheets_CellFormat_TextDirection_LeftToRight The text + * direction of left-to-right was set by the user. (Value: + * "LEFT_TO_RIGHT") + * @arg @c kGTLRSheets_CellFormat_TextDirection_RightToLeft The text + * direction of right-to-left was set by the user. (Value: + * "RIGHT_TO_LEFT") + * @arg @c kGTLRSheets_CellFormat_TextDirection_TextDirectionUnspecified The + * text direction is not specified. Do not use this. (Value: + * "TEXT_DIRECTION_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *textDirection; + +/** The format of the text in the cell (unless overridden by a format run). */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *textFormat; + +/** The rotation applied to text in a cell */ +@property(nonatomic, strong, nullable) GTLRSheets_TextRotation *textRotation; + +/** + * The vertical alignment of the value in the cell. + * + * Likely values: + * @arg @c kGTLRSheets_CellFormat_VerticalAlignment_Bottom The text is + * explicitly aligned to the bottom of the cell. (Value: "BOTTOM") + * @arg @c kGTLRSheets_CellFormat_VerticalAlignment_Middle The text is + * explicitly aligned to the middle of the cell. (Value: "MIDDLE") + * @arg @c kGTLRSheets_CellFormat_VerticalAlignment_Top The text is + * explicitly aligned to the top of the cell. (Value: "TOP") + * @arg @c kGTLRSheets_CellFormat_VerticalAlignment_VerticalAlignUnspecified + * The vertical alignment is not specified. Do not use this. (Value: + * "VERTICAL_ALIGN_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *verticalAlignment; + +/** + * The wrap strategy for the value in the cell. + * + * Likely values: + * @arg @c kGTLRSheets_CellFormat_WrapStrategy_Clip Lines that are longer + * than the cell width will be clipped. + * The text will never wrap to the next line unless the user manually + * inserts a new line. + * Example: + * | First sentence. | + * | Manual newline t| <- Text is clipped + * | Next newline. | (Value: "CLIP") + * @arg @c kGTLRSheets_CellFormat_WrapStrategy_LegacyWrap This wrap strategy + * represents the old Google Sheets wrap strategy where + * words that are longer than a line are clipped rather than broken. This + * strategy is not supported on all platforms and is being phased out. + * Example: + * | Cell has a | + * | loooooooooo| <- Word is clipped. + * | word. | (Value: "LEGACY_WRAP") + * @arg @c kGTLRSheets_CellFormat_WrapStrategy_OverflowCell Lines that are + * longer than the cell width will be written in the next + * cell over, so long as that cell is empty. If the next cell over is + * non-empty, this behaves the same as CLIP. The text will never wrap + * to the next line unless the user manually inserts a new line. + * Example: + * | First sentence. | + * | Manual newline that is very long. <- Text continues into next cell + * | Next newline. | (Value: "OVERFLOW_CELL") + * @arg @c kGTLRSheets_CellFormat_WrapStrategy_Wrap Words that are longer + * than a line are wrapped at the character level + * rather than clipped. + * Example: + * | Cell has a | + * | loooooooooo| <- Word is broken. + * | ong word. | (Value: "WRAP") + * @arg @c kGTLRSheets_CellFormat_WrapStrategy_WrapStrategyUnspecified The + * default value, do not use. (Value: "WRAP_STRATEGY_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *wrapStrategy; + +@end + + +/** + * The data included in a domain or series. + */ +@interface GTLRSheets_ChartData : GTLRObject + +/** The source ranges of the data. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartSourceRange *sourceRange; + +@end + + +/** + * Source ranges for a chart. + */ +@interface GTLRSheets_ChartSourceRange : GTLRObject + +/** + * The ranges of data for a series or domain. + * Exactly one dimension must have a length of 1, + * and all sources in the list must have the same dimension + * with length 1. + * The domain (if it exists) & all series must have the same number + * of source ranges. If using more than one source range, then the source + * range at a given offset must be in order and contiguous across the domain + * and series. + * For example, these are valid configurations: + * domain sources: A1:A5 + * series1 sources: B1:B5 + * series2 sources: D6:D10 + * domain sources: A1:A5, C10:C12 + * series1 sources: B1:B5, D10:D12 + * series2 sources: C1:C5, E10:E12 + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_GridRange *> *sources; + +@end + + +/** + * The specifications of a chart. + */ +@interface GTLRSheets_ChartSpec : GTLRObject + +/** + * The alternative text that describes the chart. This is often used + * for accessibility. + */ +@property(nonatomic, copy, nullable) NSString *altText; + +/** + * The background color of the entire chart. + * Not applicable to Org charts. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *backgroundColor; + +/** + * A basic chart specification, can be one of many kinds of charts. + * See BasicChartType for the list of all + * charts this supports. + */ +@property(nonatomic, strong, nullable) GTLRSheets_BasicChartSpec *basicChart; + +/** A bubble chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_BubbleChartSpec *bubbleChart; + +/** A candlestick chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_CandlestickChartSpec *candlestickChart; + +/** + * The name of the font to use by default for all chart text (e.g. title, + * axis labels, legend). If a font is specified for a specific part of the + * chart it will override this font name. + */ +@property(nonatomic, copy, nullable) NSString *fontName; + +/** + * Determines how the charts will use hidden rows or columns. + * + * Likely values: + * @arg @c kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ChartHiddenDimensionStrategyUnspecified + * Default value, do not use. (Value: + * "CHART_HIDDEN_DIMENSION_STRATEGY_UNSPECIFIED") + * @arg @c kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ShowAll Charts will + * not skip any hidden rows or columns. (Value: "SHOW_ALL") + * @arg @c kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenColumns + * Charts will skip hidden columns only. (Value: "SKIP_HIDDEN_COLUMNS") + * @arg @c kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRows + * Charts will skip hidden rows only. (Value: "SKIP_HIDDEN_ROWS") + * @arg @c kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRowsAndColumns + * Charts will skip hidden rows and columns. (Value: + * "SKIP_HIDDEN_ROWS_AND_COLUMNS") + */ +@property(nonatomic, copy, nullable) NSString *hiddenDimensionStrategy; + +/** A histogram chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_HistogramChartSpec *histogramChart; + +/** + * True to make a chart fill the entire space in which it's rendered with + * minimum padding. False to use the default padding. + * (Not applicable to Geo and Org charts.) + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *maximized; + +/** An org chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_OrgChartSpec *orgChart; + +/** A pie chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_PieChartSpec *pieChart; + +/** The subtitle of the chart. */ +@property(nonatomic, copy, nullable) NSString *subtitle; + +/** + * The subtitle text format. + * Strikethrough and underline are not supported. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *subtitleTextFormat; + +/** + * The subtitle text position. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextPosition *subtitleTextPosition; + +/** The title of the chart. */ +@property(nonatomic, copy, nullable) NSString *title; + +/** + * The title text format. + * Strikethrough and underline are not supported. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *titleTextFormat; + +/** + * The title text position. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TextPosition *titleTextPosition; + +/** A treemap chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_TreemapChartSpec *treemapChart; + +/** A waterfall chart specification. */ +@property(nonatomic, strong, nullable) GTLRSheets_WaterfallChartSpec *waterfallChart; + +@end + + +/** + * Clears the basic filter, if any exists on the sheet. + */ +@interface GTLRSheets_ClearBasicFilterRequest : GTLRObject + +/** + * The sheet ID on which the basic filter should be cleared. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * The request for clearing a range of values in a spreadsheet. + */ +@interface GTLRSheets_ClearValuesRequest : GTLRObject +@end + + +/** + * The response when clearing a range of values in a spreadsheet. + */ +@interface GTLRSheets_ClearValuesResponse : GTLRObject + +/** + * The range (in A1 notation) that was cleared. + * (If the request was for an unbounded range or a ranger larger + * than the bounds of the sheet, this will be the actual range + * that was cleared, bounded to the sheet's limits.) + */ +@property(nonatomic, copy, nullable) NSString *clearedRange; + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +@end + + +/** + * Represents a color in the RGBA color space. This representation is designed + * for simplicity of conversion to/from color representations in various + * languages over compactness; for example, the fields of this representation + * can be trivially provided to the constructor of "java.awt.Color" in Java; it + * can also be trivially provided to UIColor's "+colorWithRed:green:blue:alpha" + * method in iOS; and, with just a little work, it can be easily formatted into + * a CSS "rgba()" string in JavaScript, as well. Here are some examples: + * Example (Java): + * import com.google.type.Color; + * // ... + * public static java.awt.Color fromProto(Color protocolor) { + * float alpha = protocolor.hasAlpha() + * ? protocolor.getAlpha().getValue() + * : 1.0; + * return new java.awt.Color( + * protocolor.getRed(), + * protocolor.getGreen(), + * protocolor.getBlue(), + * alpha); + * } + * public static Color toProto(java.awt.Color color) { + * float red = (float) color.getRed(); + * float green = (float) color.getGreen(); + * float blue = (float) color.getBlue(); + * float denominator = 255.0; + * Color.Builder resultBuilder = + * Color + * .newBuilder() + * .setRed(red / denominator) + * .setGreen(green / denominator) + * .setBlue(blue / denominator); + * int alpha = color.getAlpha(); + * if (alpha != 255) { + * result.setAlpha( + * FloatValue + * .newBuilder() + * .setValue(((float) alpha) / denominator) + * .build()); + * } + * return resultBuilder.build(); + * } + * // ... + * Example (iOS / Obj-C): + * // ... + * static UIColor* fromProto(Color* protocolor) { + * float red = [protocolor red]; + * float green = [protocolor green]; + * float blue = [protocolor blue]; + * FloatValue* alpha_wrapper = [protocolor alpha]; + * float alpha = 1.0; + * if (alpha_wrapper != nil) { + * alpha = [alpha_wrapper value]; + * } + * return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; + * } + * static Color* toProto(UIColor* color) { + * CGFloat red, green, blue, alpha; + * if (![color getRed:&red green:&green blue:&blue alpha:&alpha]) { + * return nil; + * } + * Color* result = [[Color alloc] init]; + * [result setRed:red]; + * [result setGreen:green]; + * [result setBlue:blue]; + * if (alpha <= 0.9999) { + * [result setAlpha:floatWrapperWithValue(alpha)]; + * } + * [result autorelease]; + * return result; + * } + * // ... + * Example (JavaScript): + * // ... + * var protoToCssColor = function(rgb_color) { + * var redFrac = rgb_color.red || 0.0; + * var greenFrac = rgb_color.green || 0.0; + * var blueFrac = rgb_color.blue || 0.0; + * var red = Math.floor(redFrac * 255); + * var green = Math.floor(greenFrac * 255); + * var blue = Math.floor(blueFrac * 255); + * if (!('alpha' in rgb_color)) { + * return rgbToCssColor_(red, green, blue); + * } + * var alphaFrac = rgb_color.alpha.value || 0.0; + * var rgbParams = [red, green, blue].join(','); + * return ['rgba(', rgbParams, ',', alphaFrac, ')'].join(''); + * }; + * var rgbToCssColor_ = function(red, green, blue) { + * var rgbNumber = new Number((red << 16) | (green << 8) | blue); + * var hexString = rgbNumber.toString(16); + * var missingZeros = 6 - hexString.length; + * var resultBuilder = ['#']; + * for (var i = 0; i < missingZeros; i++) { + * resultBuilder.push('0'); + * } + * resultBuilder.push(hexString); + * return resultBuilder.join(''); + * }; + * // ... + */ +@interface GTLRSheets_Color : GTLRObject + +/** + * The fraction of this color that should be applied to the pixel. That is, + * the final pixel color is defined by the equation: + * pixel color = alpha * (this color) + (1.0 - alpha) * (background color) + * This means that a value of 1.0 corresponds to a solid color, whereas + * a value of 0.0 corresponds to a completely transparent color. This + * uses a wrapper message rather than a simple float scalar so that it is + * possible to distinguish between a default value and the value being unset. + * If omitted, this color object is to be rendered as a solid color + * (as if the alpha value had been explicitly given with a value of 1.0). + * + * Uses NSNumber of floatValue. + */ +@property(nonatomic, strong, nullable) NSNumber *alpha; + +/** + * The amount of blue in the color as a value in the interval [0, 1]. + * + * Uses NSNumber of floatValue. + */ +@property(nonatomic, strong, nullable) NSNumber *blue; + +/** + * The amount of green in the color as a value in the interval [0, 1]. + * + * Uses NSNumber of floatValue. + */ +@property(nonatomic, strong, nullable) NSNumber *green; + +/** + * The amount of red in the color as a value in the interval [0, 1]. + * + * Uses NSNumber of floatValue. + */ +@property(nonatomic, strong, nullable) NSNumber *red; + +@end + + +/** + * A rule describing a conditional format. + */ +@interface GTLRSheets_ConditionalFormatRule : GTLRObject + +/** The formatting is either "on" or "off" according to the rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_BooleanRule *booleanRule; + +/** The formatting will vary based on the gradients in the rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_GradientRule *gradientRule; + +/** + * The ranges that are formatted if the condition is true. + * All the ranges must be on the same grid. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_GridRange *> *ranges; + +@end + + +/** + * The value of the condition. + */ +@interface GTLRSheets_ConditionValue : GTLRObject + +/** + * A relative date (based on the current date). + * Valid only if the type is + * DATE_BEFORE, + * DATE_AFTER, + * DATE_ON_OR_BEFORE or + * DATE_ON_OR_AFTER. + * Relative dates are not supported in data validation. + * They are supported only in conditional formatting and + * conditional filters. + * + * Likely values: + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_PastMonth The value is one + * month before today. (Value: "PAST_MONTH") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_PastWeek The value is one + * week before today. (Value: "PAST_WEEK") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_PastYear The value is one + * year before today. (Value: "PAST_YEAR") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_RelativeDateUnspecified + * Default value, do not use. (Value: "RELATIVE_DATE_UNSPECIFIED") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_Today The value is today. + * (Value: "TODAY") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_Tomorrow The value is + * tomorrow. (Value: "TOMORROW") + * @arg @c kGTLRSheets_ConditionValue_RelativeDate_Yesterday The value is + * yesterday. (Value: "YESTERDAY") + */ +@property(nonatomic, copy, nullable) NSString *relativeDate; + +/** + * A value the condition is based on. + * The value is parsed as if the user typed into a cell. + * Formulas are supported (and must begin with an `=` or a '+'). + */ +@property(nonatomic, copy, nullable) NSString *userEnteredValue; + +@end + + +/** + * Copies data from the source to the destination. + */ +@interface GTLRSheets_CopyPasteRequest : GTLRObject + +/** + * The location to paste to. If the range covers a span that's + * a multiple of the source's height or width, then the + * data will be repeated to fill in the destination range. + * If the range is smaller than the source range, the entire + * source data will still be copied (beyond the end of the destination range). + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *destination; + +/** + * How that data should be oriented when pasting. + * + * Likely values: + * @arg @c kGTLRSheets_CopyPasteRequest_PasteOrientation_Normal Paste + * normally. (Value: "NORMAL") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteOrientation_Transpose Paste + * transposed, where all rows become columns and vice versa. (Value: + * "TRANSPOSE") + */ +@property(nonatomic, copy, nullable) NSString *pasteOrientation; + +/** + * What kind of data to paste. + * + * Likely values: + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteConditionalFormatting + * Paste the conditional formatting rules only. (Value: + * "PASTE_CONDITIONAL_FORMATTING") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteDataValidation Paste + * the data validation only. (Value: "PASTE_DATA_VALIDATION") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteFormat Paste the + * format and data validation only. (Value: "PASTE_FORMAT") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteFormula Paste the + * formulas only. (Value: "PASTE_FORMULA") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteNoBorders Like + * PASTE_NORMAL but without borders. (Value: "PASTE_NO_BORDERS") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteNormal Paste values, + * formulas, formats, and merges. (Value: "PASTE_NORMAL") + * @arg @c kGTLRSheets_CopyPasteRequest_PasteType_PasteValues Paste the + * values ONLY without formats, formulas, or merges. (Value: + * "PASTE_VALUES") + */ +@property(nonatomic, copy, nullable) NSString *pasteType; + +/** The source range to copy. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *source; + +@end + + +/** + * The request to copy a sheet across spreadsheets. + */ +@interface GTLRSheets_CopySheetToAnotherSpreadsheetRequest : GTLRObject + +/** The ID of the spreadsheet to copy the sheet to. */ +@property(nonatomic, copy, nullable) NSString *destinationSpreadsheetId; + +@end + + +/** + * A request to create developer metadata. + */ +@interface GTLRSheets_CreateDeveloperMetadataRequest : GTLRObject + +/** The developer metadata to create. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadata *developerMetadata; + +@end + + +/** + * The response from creating developer metadata. + */ +@interface GTLRSheets_CreateDeveloperMetadataResponse : GTLRObject + +/** The developer metadata that was created. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadata *developerMetadata; + +@end + + +/** + * Moves data from the source to the destination. + */ +@interface GTLRSheets_CutPasteRequest : GTLRObject + +/** The top-left coordinate where the data should be pasted. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridCoordinate *destination; + +/** + * What kind of data to paste. All the source data will be cut, regardless + * of what is pasted. + * + * Likely values: + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteConditionalFormatting + * Paste the conditional formatting rules only. (Value: + * "PASTE_CONDITIONAL_FORMATTING") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteDataValidation Paste + * the data validation only. (Value: "PASTE_DATA_VALIDATION") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteFormat Paste the format + * and data validation only. (Value: "PASTE_FORMAT") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteFormula Paste the + * formulas only. (Value: "PASTE_FORMULA") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteNoBorders Like + * PASTE_NORMAL but without borders. (Value: "PASTE_NO_BORDERS") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteNormal Paste values, + * formulas, formats, and merges. (Value: "PASTE_NORMAL") + * @arg @c kGTLRSheets_CutPasteRequest_PasteType_PasteValues Paste the values + * ONLY without formats, formulas, or merges. (Value: "PASTE_VALUES") + */ +@property(nonatomic, copy, nullable) NSString *pasteType; + +/** The source data to cut. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *source; + +@end + + +/** + * Filter that describes what data should be selected or returned from a + * request. + */ +@interface GTLRSheets_DataFilter : GTLRObject + +/** Selects data that matches the specified A1 range. */ +@property(nonatomic, copy, nullable) NSString *a1Range; + +/** + * Selects data associated with the developer metadata matching the criteria + * described by this DeveloperMetadataLookup. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadataLookup *developerMetadataLookup; + +/** Selects data that matches the range described by the GridRange. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *gridRange; + +@end + + +/** + * A range of values whose location is specified by a DataFilter. + */ +@interface GTLRSheets_DataFilterValueRange : GTLRObject + +/** + * The data filter describing the location of the values in the spreadsheet. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DataFilter *dataFilter; + +/** + * The major dimension of the values. + * + * Likely values: + * @arg @c kGTLRSheets_DataFilterValueRange_MajorDimension_Columns Operates + * on the columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_DataFilterValueRange_MajorDimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_DataFilterValueRange_MajorDimension_Rows Operates on + * the rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *majorDimension; + +/** + * The data to be written. If the provided values exceed any of the ranges + * matched by the data filter then the request will fail. If the provided + * values are less than the matched ranges only the specified values will be + * written, existing values in the matched ranges will remain unaffected. + * + * Can be any valid JSON type. + */ +@property(nonatomic, strong, nullable) NSArray<NSArray *> *values; + +@end + + +/** + * A data validation rule. + */ +@interface GTLRSheets_DataValidationRule : GTLRObject + +/** The condition that data in the cell must match. */ +@property(nonatomic, strong, nullable) GTLRSheets_BooleanCondition *condition; + +/** A message to show the user when adding data to the cell. */ +@property(nonatomic, copy, nullable) NSString *inputMessage; + +/** + * True if the UI should be customized based on the kind of condition. + * If true, "List" conditions will show a dropdown. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *showCustomUi; + +/** + * True if invalid data should be rejected. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *strict; + +@end + + +/** + * Allows you to organize the date-time values in a source data column into + * buckets based on selected parts of their date or time values. For example, + * consider a pivot table showing sales transactions by date: + * +----------+--------------+ + * | Date | SUM of Sales | + * +----------+--------------+ + * | 1/1/2017 | $621.14 | + * | 2/3/2017 | $708.84 | + * | 5/8/2017 | $326.84 | + * ... + * +----------+--------------+ + * Applying a date-time group rule with a DateTimeRuleType of YEAR_MONTH + * results in the following pivot table. + * +--------------+--------------+ + * | Grouped Date | SUM of Sales | + * +--------------+--------------+ + * | 2017-Jan | $53,731.78 | + * | 2017-Feb | $83,475.32 | + * | 2017-Mar | $94,385.05 | + * ... + * +--------------+--------------+ + */ +@interface GTLRSheets_DateTimeRule : GTLRObject + +/** + * The type of date-time grouping to apply. + * + * Likely values: + * @arg @c kGTLRSheets_DateTimeRule_Type_DateTimeRuleTypeUnspecified The + * default type, do not use. (Value: "DATE_TIME_RULE_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_DateTimeRule_Type_DayMonth Group dates by day and + * month, for example 22-Nov. The month is + * translated based on the spreadsheet locale. (Value: "DAY_MONTH") + * @arg @c kGTLRSheets_DateTimeRule_Type_DayOfMonth Group dates by day of + * month, from 1 to 31. (Value: "DAY_OF_MONTH") + * @arg @c kGTLRSheets_DateTimeRule_Type_DayOfWeek Group dates by day of + * week, for example Sunday. The days of the week will + * be translated based on the spreadsheet locale. (Value: "DAY_OF_WEEK") + * @arg @c kGTLRSheets_DateTimeRule_Type_DayOfYear Group dates by day of + * year, from 1 to 366. Note that dates after Feb. 29 + * fall in different buckets in leap years than in non-leap years. + * (Value: "DAY_OF_YEAR") + * @arg @c kGTLRSheets_DateTimeRule_Type_Hour Group dates by hour using a + * 24-hour system, from 0 to 23. (Value: "HOUR") + * @arg @c kGTLRSheets_DateTimeRule_Type_HourMinute Group dates by hour and + * minute using a 24-hour system, for example 19:45. (Value: + * "HOUR_MINUTE") + * @arg @c kGTLRSheets_DateTimeRule_Type_HourMinuteAmpm Group dates by hour + * and minute using a 12-hour system, for example 7:45 + * PM. The AM/PM designation is translated based on the spreadsheet + * locale. (Value: "HOUR_MINUTE_AMPM") + * @arg @c kGTLRSheets_DateTimeRule_Type_Minute Group dates by minute, from 0 + * to 59. (Value: "MINUTE") + * @arg @c kGTLRSheets_DateTimeRule_Type_Month Group dates by month, for + * example Nov. The month is translated based + * on the spreadsheet locale. (Value: "MONTH") + * @arg @c kGTLRSheets_DateTimeRule_Type_Quarter Group dates by quarter, for + * example Q1 (which represents Jan-Mar). (Value: "QUARTER") + * @arg @c kGTLRSheets_DateTimeRule_Type_Second Group dates by second, from 0 + * to 59. (Value: "SECOND") + * @arg @c kGTLRSheets_DateTimeRule_Type_Year Group dates by year, for + * example 2008. (Value: "YEAR") + * @arg @c kGTLRSheets_DateTimeRule_Type_YearMonth Group dates by year and + * month, for example 2008-Nov. The month is + * translated based on the spreadsheet locale. (Value: "YEAR_MONTH") + * @arg @c kGTLRSheets_DateTimeRule_Type_YearMonthDay Group dates by year, + * month, and day, for example 2008-11-22. (Value: "YEAR_MONTH_DAY") + * @arg @c kGTLRSheets_DateTimeRule_Type_YearQuarter Group dates by year and + * quarter, for example 2008 Q4. (Value: "YEAR_QUARTER") + */ +@property(nonatomic, copy, nullable) NSString *type; + +@end + + +/** + * Removes the banded range with the given ID from the spreadsheet. + */ +@interface GTLRSheets_DeleteBandingRequest : GTLRObject + +/** + * The ID of the banded range to delete. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bandedRangeId; + +@end + + +/** + * Deletes a conditional format rule at the given index. + * All subsequent rules' indexes are decremented. + */ +@interface GTLRSheets_DeleteConditionalFormatRuleRequest : GTLRObject + +/** + * The zero-based index of the rule to be deleted. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *index; + +/** + * The sheet the rule is being deleted from. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * The result of deleting a conditional format rule. + */ +@interface GTLRSheets_DeleteConditionalFormatRuleResponse : GTLRObject + +/** The rule that was deleted. */ +@property(nonatomic, strong, nullable) GTLRSheets_ConditionalFormatRule *rule; + +@end + + +/** + * A request to delete developer metadata. + */ +@interface GTLRSheets_DeleteDeveloperMetadataRequest : GTLRObject + +/** + * The data filter describing the criteria used to select which developer + * metadata entry to delete. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DataFilter *dataFilter; + +@end + + +/** + * The response from deleting developer metadata. + */ +@interface GTLRSheets_DeleteDeveloperMetadataResponse : GTLRObject + +/** The metadata that was deleted. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DeveloperMetadata *> *deletedDeveloperMetadata; + +@end + + +/** + * Deletes a group over the specified range by decrementing the depth of the + * dimensions in the range. + * For example, assume the sheet has a depth-1 group over B:E and a depth-2 + * group over C:D. Deleting a group over D:E leaves the sheet with a + * depth-1 group over B:D and a depth-2 group over C:C. + */ +@interface GTLRSheets_DeleteDimensionGroupRequest : GTLRObject + +/** The range of the group to be deleted. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * The result of deleting a group. + */ +@interface GTLRSheets_DeleteDimensionGroupResponse : GTLRObject + +/** All groups of a dimension after deleting a group from that dimension. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionGroup *> *dimensionGroups; + +@end + + +/** + * Deletes the dimensions from the sheet. + */ +@interface GTLRSheets_DeleteDimensionRequest : GTLRObject + +/** The dimensions to delete from the sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * Deletes the embedded object with the given ID. + */ +@interface GTLRSheets_DeleteEmbeddedObjectRequest : GTLRObject + +/** + * The ID of the embedded object to delete. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *objectId; + +@end + + +/** + * Deletes a particular filter view. + */ +@interface GTLRSheets_DeleteFilterViewRequest : GTLRObject + +/** + * The ID of the filter to delete. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *filterId; + +@end + + +/** + * Removes the named range with the given ID from the spreadsheet. + */ +@interface GTLRSheets_DeleteNamedRangeRequest : GTLRObject + +/** The ID of the named range to delete. */ +@property(nonatomic, copy, nullable) NSString *namedRangeId; + +@end + + +/** + * Deletes the protected range with the given ID. + */ +@interface GTLRSheets_DeleteProtectedRangeRequest : GTLRObject + +/** + * The ID of the protected range to delete. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *protectedRangeId; + +@end + + +/** + * Deletes a range of cells, shifting other cells into the deleted area. + */ +@interface GTLRSheets_DeleteRangeRequest : GTLRObject + +/** The range of cells to delete. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The dimension from which deleted cells will be replaced with. + * If ROWS, existing cells will be shifted upward to + * replace the deleted cells. If COLUMNS, existing cells + * will be shifted left to replace the deleted cells. + * + * Likely values: + * @arg @c kGTLRSheets_DeleteRangeRequest_ShiftDimension_Columns Operates on + * the columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_DeleteRangeRequest_ShiftDimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_DeleteRangeRequest_ShiftDimension_Rows Operates on the + * rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *shiftDimension; + +@end + + +/** + * Deletes the requested sheet. + */ +@interface GTLRSheets_DeleteSheetRequest : GTLRObject + +/** + * The ID of the sheet to delete. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * Developer metadata associated with a location or object in a spreadsheet. + * Developer metadata may be used to associate arbitrary data with various + * parts of a spreadsheet and will remain associated at those locations as they + * move around and the spreadsheet is edited. For example, if developer + * metadata is associated with row 5 and another row is then subsequently + * inserted above row 5, that original metadata will still be associated with + * the row it was first associated with (what is now row 6). If the associated + * object is deleted its metadata is deleted too. + */ +@interface GTLRSheets_DeveloperMetadata : GTLRObject + +/** The location where the metadata is associated. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadataLocation *location; + +/** + * The spreadsheet-scoped unique ID that identifies the metadata. IDs may be + * specified when metadata is created, otherwise one will be randomly + * generated and assigned. Must be positive. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *metadataId; + +/** + * The metadata key. There may be multiple metadata in a spreadsheet with the + * same key. Developer metadata must always have a key specified. + */ +@property(nonatomic, copy, nullable) NSString *metadataKey; + +/** Data associated with the metadata's key. */ +@property(nonatomic, copy, nullable) NSString *metadataValue; + +/** + * The metadata visibility. Developer metadata must always have a visibility + * specified. + * + * Likely values: + * @arg @c kGTLRSheets_DeveloperMetadata_Visibility_DeveloperMetadataVisibilityUnspecified + * Default value. (Value: "DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED") + * @arg @c kGTLRSheets_DeveloperMetadata_Visibility_Document Document-visible + * metadata is accessible from any developer project with + * access to the document. (Value: "DOCUMENT") + * @arg @c kGTLRSheets_DeveloperMetadata_Visibility_Project Project-visible + * metadata is only visible to and accessible by the developer + * project that created the metadata. (Value: "PROJECT") + */ +@property(nonatomic, copy, nullable) NSString *visibility; + +@end + + +/** + * A location where metadata may be associated in a spreadsheet. + */ +@interface GTLRSheets_DeveloperMetadataLocation : GTLRObject + +/** + * Represents the row or column when metadata is associated with + * a dimension. The specified DimensionRange must represent a single row + * or column; it cannot be unbounded or span multiple rows or columns. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *dimensionRange; + +/** + * The type of location this object represents. This field is read-only. + * + * Likely values: + * @arg @c kGTLRSheets_DeveloperMetadataLocation_LocationType_Column + * Developer metadata associated on an entire column dimension. (Value: + * "COLUMN") + * @arg @c kGTLRSheets_DeveloperMetadataLocation_LocationType_DeveloperMetadataLocationTypeUnspecified + * Default value. (Value: "DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_DeveloperMetadataLocation_LocationType_Row Developer + * metadata associated on an entire row dimension. (Value: "ROW") + * @arg @c kGTLRSheets_DeveloperMetadataLocation_LocationType_Sheet Developer + * metadata associated on an entire sheet. (Value: "SHEET") + * @arg @c kGTLRSheets_DeveloperMetadataLocation_LocationType_Spreadsheet + * Developer metadata associated on the entire spreadsheet. (Value: + * "SPREADSHEET") + */ +@property(nonatomic, copy, nullable) NSString *locationType; + +/** + * The ID of the sheet when metadata is associated with an entire sheet. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +/** + * True when metadata is associated with an entire spreadsheet. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *spreadsheet; + +@end + + +/** + * Selects DeveloperMetadata that matches all of the specified fields. For + * example, if only a metadata ID is specified this considers the + * DeveloperMetadata with that particular unique ID. If a metadata key is + * specified, this considers all developer metadata with that key. If a + * key, visibility, and location type are all specified, this considers all + * developer metadata with that key and visibility that are associated with a + * location of that type. In general, this + * selects all DeveloperMetadata that matches the intersection of all the + * specified fields; any field or combination of fields may be specified. + */ +@interface GTLRSheets_DeveloperMetadataLookup : GTLRObject + +/** + * Determines how this lookup matches the location. If this field is + * specified as EXACT, only developer metadata associated on the exact + * location specified is matched. If this field is specified to INTERSECTING, + * developer metadata associated on intersecting locations is also + * matched. If left unspecified, this field assumes a default value of + * INTERSECTING. + * If this field is specified, a metadataLocation + * must also be specified. + * + * Likely values: + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_DeveloperMetadataLocationMatchingStrategyUnspecified + * Default value. This value must not be used. (Value: + * "DEVELOPER_METADATA_LOCATION_MATCHING_STRATEGY_UNSPECIFIED") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_ExactLocation + * Indicates that a specified location should be matched exactly. For + * example, if row three were specified as a location this matching + * strategy + * would only match developer metadata also associated on row three. + * Metadata + * associated on other locations would not be considered. (Value: + * "EXACT_LOCATION") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_IntersectingLocation + * Indicates that a specified location should match that exact location + * as + * well as any intersecting locations. For example, if row three were + * specified as a location this matching strategy would match developer + * metadata associated on row three as well as metadata associated on + * locations that intersect row three. If, for instance, there was + * developer + * metadata associated on column B, this matching strategy would also + * match + * that location because column B intersects row three. (Value: + * "INTERSECTING_LOCATION") + */ +@property(nonatomic, copy, nullable) NSString *locationMatchingStrategy; + +/** + * Limits the selected developer metadata to those entries which are + * associated with locations of the specified type. For example, when this + * field is specified as ROW this lookup + * only considers developer metadata associated on rows. If the field is left + * unspecified, all location types are considered. This field cannot be + * specified as SPREADSHEET when + * the locationMatchingStrategy + * is specified as INTERSECTING or when the + * metadataLocation is specified as a + * non-spreadsheet location: spreadsheet metadata cannot intersect any other + * developer metadata location. This field also must be left unspecified when + * the locationMatchingStrategy + * is specified as EXACT. + * + * Likely values: + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationType_Column Developer + * metadata associated on an entire column dimension. (Value: "COLUMN") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationType_DeveloperMetadataLocationTypeUnspecified + * Default value. (Value: "DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationType_Row Developer + * metadata associated on an entire row dimension. (Value: "ROW") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationType_Sheet Developer + * metadata associated on an entire sheet. (Value: "SHEET") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_LocationType_Spreadsheet + * Developer metadata associated on the entire spreadsheet. (Value: + * "SPREADSHEET") + */ +@property(nonatomic, copy, nullable) NSString *locationType; + +/** + * Limits the selected developer metadata to that which has a matching + * DeveloperMetadata.metadata_id. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *metadataId; + +/** + * Limits the selected developer metadata to that which has a matching + * DeveloperMetadata.metadata_key. + */ +@property(nonatomic, copy, nullable) NSString *metadataKey; + +/** + * Limits the selected developer metadata to those entries associated with + * the specified location. This field either matches exact locations or all + * intersecting locations according the specified + * locationMatchingStrategy. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadataLocation *metadataLocation; + +/** + * Limits the selected developer metadata to that which has a matching + * DeveloperMetadata.metadata_value. + */ +@property(nonatomic, copy, nullable) NSString *metadataValue; + +/** + * Limits the selected developer metadata to that which has a matching + * DeveloperMetadata.visibility. If left unspecified, all developer + * metadata visibile to the requesting project is considered. + * + * Likely values: + * @arg @c kGTLRSheets_DeveloperMetadataLookup_Visibility_DeveloperMetadataVisibilityUnspecified + * Default value. (Value: "DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_Visibility_Document + * Document-visible metadata is accessible from any developer project + * with + * access to the document. (Value: "DOCUMENT") + * @arg @c kGTLRSheets_DeveloperMetadataLookup_Visibility_Project + * Project-visible metadata is only visible to and accessible by the + * developer + * project that created the metadata. (Value: "PROJECT") + */ +@property(nonatomic, copy, nullable) NSString *visibility; + +@end + + +/** + * A group over an interval of rows or columns on a sheet, which can contain or + * be contained within other groups. A group can be collapsed or expanded as a + * unit on the sheet. + */ +@interface GTLRSheets_DimensionGroup : GTLRObject + +/** + * This field is true if this group is collapsed. A collapsed group remains + * collapsed if an overlapping group at a shallower depth is expanded. + * A true value does not imply that all dimensions within the group are + * hidden, since a dimension's visibility can change independently from this + * group property. However, when this property is updated, all dimensions + * within it are set to hidden if this field is true, or set to visible if + * this field is false. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *collapsed; + +/** + * The depth of the group, representing how many groups have a range that + * wholly contains the range of this group. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *depth; + +/** The range over which this group exists. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * Properties about a dimension. + */ +@interface GTLRSheets_DimensionProperties : GTLRObject + +/** The developer metadata associated with a single row or column. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DeveloperMetadata *> *developerMetadata; + +/** + * True if this dimension is being filtered. + * This field is read-only. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hiddenByFilter; + +/** + * True if this dimension is explicitly hidden. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hiddenByUser; + +/** + * The height (if a row) or width (if a column) of the dimension in pixels. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *pixelSize; + +@end + + +/** + * A range along a single dimension on a sheet. + * All indexes are zero-based. + * Indexes are half open: the start index is inclusive + * and the end index is exclusive. + * Missing indexes indicate the range is unbounded on that side. + */ +@interface GTLRSheets_DimensionRange : GTLRObject + +/** + * The dimension of the span. + * + * Likely values: + * @arg @c kGTLRSheets_DimensionRange_Dimension_Columns Operates on the + * columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_DimensionRange_Dimension_DimensionUnspecified The + * default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_DimensionRange_Dimension_Rows Operates on the rows of + * a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *dimension; + +/** + * The end (exclusive) of the span, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *endIndex; + +/** + * The sheet this span is on. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +/** + * The start (inclusive) of the span, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startIndex; + +@end + + +/** + * Duplicates a particular filter view. + */ +@interface GTLRSheets_DuplicateFilterViewRequest : GTLRObject + +/** + * The ID of the filter being duplicated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *filterId; + +@end + + +/** + * The result of a filter view being duplicated. + */ +@interface GTLRSheets_DuplicateFilterViewResponse : GTLRObject + +/** The newly created filter. */ +@property(nonatomic, strong, nullable) GTLRSheets_FilterView *filter; + +@end + + +/** + * Duplicates the contents of a sheet. + */ +@interface GTLRSheets_DuplicateSheetRequest : GTLRObject + +/** + * The zero-based index where the new sheet should be inserted. + * The index of all sheets after this are incremented. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *insertSheetIndex; + +/** + * If set, the ID of the new sheet. If not set, an ID is chosen. + * If set, the ID must not conflict with any existing sheet ID. + * If set, it must be non-negative. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *newSheetId NS_RETURNS_NOT_RETAINED; + +/** The name of the new sheet. If empty, a new name is chosen for you. */ +@property(nonatomic, copy, nullable) NSString *newSheetName NS_RETURNS_NOT_RETAINED; + +/** + * The sheet to duplicate. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sourceSheetId; + +@end + + +/** + * The result of duplicating a sheet. + */ +@interface GTLRSheets_DuplicateSheetResponse : GTLRObject + +/** The properties of the duplicate sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_SheetProperties *properties; + +@end + + +/** + * The editors of a protected range. + */ +@interface GTLRSheets_Editors : GTLRObject + +/** + * True if anyone in the document's domain has edit access to the protected + * range. Domain protection is only supported on documents within a domain. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *domainUsersCanEdit; + +/** The email addresses of groups with edit access to the protected range. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *groups; + +/** The email addresses of users with edit access to the protected range. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *users; + +@end + + +/** + * A chart embedded in a sheet. + */ +@interface GTLRSheets_EmbeddedChart : GTLRObject + +/** + * The ID of the chart. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *chartId; + +/** The position of the chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_EmbeddedObjectPosition *position; + +/** The specification of the chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartSpec *spec; + +@end + + +/** + * The position of an embedded object such as a chart. + */ +@interface GTLRSheets_EmbeddedObjectPosition : GTLRObject + +/** + * If true, the embedded object is put on a new sheet whose ID + * is chosen for you. Used only when writing. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *newSheet NS_RETURNS_NOT_RETAINED; + +/** The position at which the object is overlaid on top of a grid. */ +@property(nonatomic, strong, nullable) GTLRSheets_OverlayPosition *overlayPosition; + +/** + * The sheet this is on. Set only if the embedded object + * is on its own sheet. Must be non-negative. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * An error in a cell. + */ +@interface GTLRSheets_ErrorValue : GTLRObject + +/** + * A message with more information about the error + * (in the spreadsheet's locale). + */ +@property(nonatomic, copy, nullable) NSString *message; + +/** + * The type of error. + * + * Likely values: + * @arg @c kGTLRSheets_ErrorValue_Type_DivideByZero Corresponds to the + * `#DIV/0` error. (Value: "DIVIDE_BY_ZERO") + * @arg @c kGTLRSheets_ErrorValue_Type_Error Corresponds to the `#ERROR!` + * error. (Value: "ERROR") + * @arg @c kGTLRSheets_ErrorValue_Type_ErrorTypeUnspecified The default error + * type, do not use this. (Value: "ERROR_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_ErrorValue_Type_Loading Corresponds to the + * `Loading...` state. (Value: "LOADING") + * @arg @c kGTLRSheets_ErrorValue_Type_NA Corresponds to the `#N/A` error. + * (Value: "N_A") + * @arg @c kGTLRSheets_ErrorValue_Type_Name Corresponds to the `#NAME?` + * error. (Value: "NAME") + * @arg @c kGTLRSheets_ErrorValue_Type_NullValue Corresponds to the `#NULL!` + * error. (Value: "NULL_VALUE") + * @arg @c kGTLRSheets_ErrorValue_Type_Num Corresponds to the `#NUM`! error. + * (Value: "NUM") + * @arg @c kGTLRSheets_ErrorValue_Type_Ref Corresponds to the `#REF!` error. + * (Value: "REF") + * @arg @c kGTLRSheets_ErrorValue_Type_Value Corresponds to the `#VALUE!` + * error. (Value: "VALUE") + */ +@property(nonatomic, copy, nullable) NSString *type; + +@end + + +/** + * The kinds of value that a cell in a spreadsheet can have. + */ +@interface GTLRSheets_ExtendedValue : GTLRObject + +/** + * Represents a boolean value. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *boolValue; + +/** + * Represents an error. + * This field is read-only. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ErrorValue *errorValue; + +/** Represents a formula. */ +@property(nonatomic, copy, nullable) NSString *formulaValue; + +/** + * Represents a double value. + * Note: Dates, Times and DateTimes are represented as doubles in + * "serial number" format. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *numberValue; + +/** + * Represents a string value. + * Leading single quotes are not included. For example, if the user typed + * `'123` into the UI, this would be represented as a `stringValue` of + * `"123"`. + */ +@property(nonatomic, copy, nullable) NSString *stringValue; + +@end + + +/** + * Criteria for showing/hiding rows in a filter or filter view. + */ +@interface GTLRSheets_FilterCriteria : GTLRObject + +/** + * A condition that must be true for values to be shown. + * (This does not override hiddenValues -- if a value is listed there, + * it will still be hidden.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_BooleanCondition *condition; + +/** Values that should be hidden. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *hiddenValues; + +@end + + +/** + * A filter view. + */ +@interface GTLRSheets_FilterView : GTLRObject + +/** + * The criteria for showing/hiding values per column. + * The map's key is the column index, and the value is the criteria for + * that column. + */ +@property(nonatomic, strong, nullable) GTLRSheets_FilterView_Criteria *criteria; + +/** + * The ID of the filter view. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *filterViewId; + +/** + * The named range this filter view is backed by, if any. + * When writing, only one of range or named_range_id + * may be set. + */ +@property(nonatomic, copy, nullable) NSString *namedRangeId; + +/** + * The range this filter view covers. + * When writing, only one of range or named_range_id + * may be set. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The sort order per column. Later specifications are used when values + * are equal in the earlier specifications. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_SortSpec *> *sortSpecs; + +/** The name of the filter view. */ +@property(nonatomic, copy, nullable) NSString *title; + +@end + + +/** + * The criteria for showing/hiding values per column. + * The map's key is the column index, and the value is the criteria for + * that column. + * + * @note This class is documented as having more properties of + * GTLRSheets_FilterCriteria. Use @c -additionalJSONKeys and @c + * -additionalPropertyForName: to get the list of properties and then + * fetch them; or @c -additionalProperties to fetch them all at once. + */ +@interface GTLRSheets_FilterView_Criteria : GTLRObject +@end + + +/** + * Finds and replaces data in cells over a range, sheet, or all sheets. + */ +@interface GTLRSheets_FindReplaceRequest : GTLRObject + +/** + * True to find/replace over all sheets. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *allSheets; + +/** The value to search. */ +@property(nonatomic, copy, nullable) NSString *find; + +/** + * True if the search should include cells with formulas. + * False to skip cells with formulas. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *includeFormulas; + +/** + * True if the search is case sensitive. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *matchCase; + +/** + * True if the find value should match the entire cell. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *matchEntireCell; + +/** The range to find/replace over. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** The value to use as the replacement. */ +@property(nonatomic, copy, nullable) NSString *replacement; + +/** + * True if the find value is a regex. + * The regular expression and replacement should follow Java regex rules + * at https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html. + * The replacement string is allowed to refer to capturing groups. + * For example, if one cell has the contents `"Google Sheets"` and another + * has `"Google Docs"`, then searching for `"o.* (.*)"` with a replacement of + * `"$1 Rocks"` would change the contents of the cells to + * `"GSheets Rocks"` and `"GDocs Rocks"` respectively. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *searchByRegex; + +/** + * The sheet to find/replace over. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * The result of the find/replace. + */ +@interface GTLRSheets_FindReplaceResponse : GTLRObject + +/** + * The number of formula cells changed. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *formulasChanged; + +/** + * The number of occurrences (possibly multiple within a cell) changed. + * For example, if replacing `"e"` with `"o"` in `"Google Sheets"`, this would + * be `"3"` because `"Google Sheets"` -> `"Googlo Shoots"`. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *occurrencesChanged; + +/** + * The number of rows changed. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *rowsChanged; + +/** + * The number of sheets changed. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetsChanged; + +/** + * The number of non-formula cells changed. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *valuesChanged; + +@end + + +/** + * The request for retrieving a Spreadsheet. + */ +@interface GTLRSheets_GetSpreadsheetByDataFilterRequest : GTLRObject + +/** + * The DataFilters used to select which ranges to retrieve from + * the spreadsheet. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +/** + * True if grid data should be returned. + * This parameter is ignored if a field mask was set in the request. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *includeGridData; + +@end + + +/** + * A rule that applies a gradient color scale format, based on + * the interpolation points listed. The format of a cell will vary + * based on its contents as compared to the values of the interpolation + * points. + */ +@interface GTLRSheets_GradientRule : GTLRObject + +/** The final interpolation point. */ +@property(nonatomic, strong, nullable) GTLRSheets_InterpolationPoint *maxpoint; + +/** An optional midway interpolation point. */ +@property(nonatomic, strong, nullable) GTLRSheets_InterpolationPoint *midpoint; + +/** The starting interpolation point. */ +@property(nonatomic, strong, nullable) GTLRSheets_InterpolationPoint *minpoint; + +@end + + +/** + * A coordinate in a sheet. + * All indexes are zero-based. + */ +@interface GTLRSheets_GridCoordinate : GTLRObject + +/** + * The column index of the coordinate. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *columnIndex; + +/** + * The row index of the coordinate. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *rowIndex; + +/** + * The sheet this coordinate is on. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * Data in the grid, as well as metadata about the dimensions. + */ +@interface GTLRSheets_GridData : GTLRObject + +/** + * Metadata about the requested columns in the grid, starting with the column + * in start_column. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionProperties *> *columnMetadata; + +/** + * The data in the grid, one entry per row, + * starting with the row in startRow. + * The values in RowData will correspond to columns starting + * at start_column. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_RowData *> *rowData; + +/** + * Metadata about the requested rows in the grid, starting with the row + * in start_row. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionProperties *> *rowMetadata; + +/** + * The first column this GridData refers to, zero-based. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startColumn; + +/** + * The first row this GridData refers to, zero-based. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startRow; + +@end + + +/** + * Properties of a grid. + */ +@interface GTLRSheets_GridProperties : GTLRObject + +/** + * The number of columns in the grid. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *columnCount; + +/** + * True if the column grouping control toggle is shown after the group. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *columnGroupControlAfter; + +/** + * The number of columns that are frozen in the grid. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *frozenColumnCount; + +/** + * The number of rows that are frozen in the grid. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *frozenRowCount; + +/** + * True if the grid isn't showing gridlines in the UI. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hideGridlines; + +/** + * The number of rows in the grid. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *rowCount; + +/** + * True if the row grouping control toggle is shown after the group. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *rowGroupControlAfter; + +@end + + +/** + * A range on a sheet. + * All indexes are zero-based. + * Indexes are half open, e.g the start index is inclusive + * and the end index is exclusive -- [start_index, end_index). + * Missing indexes indicate the range is unbounded on that side. + * For example, if `"Sheet1"` is sheet ID 0, then: + * `Sheet1!A1:A1 == sheet_id: 0, + * start_row_index: 0, end_row_index: 1, + * start_column_index: 0, end_column_index: 1` + * `Sheet1!A3:B4 == sheet_id: 0, + * start_row_index: 2, end_row_index: 4, + * start_column_index: 0, end_column_index: 2` + * `Sheet1!A:B == sheet_id: 0, + * start_column_index: 0, end_column_index: 2` + * `Sheet1!A5:B == sheet_id: 0, + * start_row_index: 4, + * start_column_index: 0, end_column_index: 2` + * `Sheet1 == sheet_id:0` + * The start index must always be less than or equal to the end index. + * If the start index equals the end index, then the range is empty. + * Empty ranges are typically not meaningful and are usually rendered in the + * UI as `#REF!`. + */ +@interface GTLRSheets_GridRange : GTLRObject + +/** + * The end column (exclusive) of the range, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *endColumnIndex; + +/** + * The end row (exclusive) of the range, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *endRowIndex; + +/** + * The sheet this range is on. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +/** + * The start column (inclusive) of the range, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startColumnIndex; + +/** + * The start row (inclusive) of the range, or not set if unbounded. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startRowIndex; + +@end + + +/** + * A <a href="/chart/interactive/docs/gallery/histogram">histogram chart</a>. + * A histogram chart groups data items into bins, displaying each bin as a + * column of stacked items. Histograms are used to display the distribution + * of a dataset. Each column of items represents a range into which those + * items fall. The number of bins can be chosen automatically or specified + * explicitly. + */ +@interface GTLRSheets_HistogramChartSpec : GTLRObject + +/** + * By default the bucket size (the range of values stacked in a single + * column) is chosen automatically, but it may be overridden here. + * E.g., A bucket size of 1.5 results in buckets from 0 - 1.5, 1.5 - 3.0, etc. + * Cannot be negative. + * This field is optional. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bucketSize; + +/** + * The position of the chart legend. + * + * Likely values: + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_BottomLegend The + * legend is rendered on the bottom of the chart. (Value: + * "BOTTOM_LEGEND") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_HistogramChartLegendPositionUnspecified + * Default value, do not use. (Value: + * "HISTOGRAM_CHART_LEGEND_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_InsideLegend The + * legend is rendered inside the chart area. (Value: "INSIDE_LEGEND") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_LeftLegend The + * legend is rendered on the left of the chart. (Value: "LEFT_LEGEND") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_NoLegend No legend + * is rendered. (Value: "NO_LEGEND") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_RightLegend The + * legend is rendered on the right of the chart. (Value: "RIGHT_LEGEND") + * @arg @c kGTLRSheets_HistogramChartSpec_LegendPosition_TopLegend The legend + * is rendered on the top of the chart. (Value: "TOP_LEGEND") + */ +@property(nonatomic, copy, nullable) NSString *legendPosition; + +/** + * The outlier percentile is used to ensure that outliers do not adversely + * affect the calculation of bucket sizes. For example, setting an outlier + * percentile of 0.05 indicates that the top and bottom 5% of values when + * calculating buckets. The values are still included in the chart, they will + * be added to the first or last buckets instead of their own buckets. + * Must be between 0.0 and 0.5. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *outlierPercentile; + +/** + * The series for a histogram may be either a single series of values to be + * bucketed or multiple series, each of the same length, containing the name + * of the series followed by the values to be bucketed for that series. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_HistogramSeries *> *series; + +/** + * Whether horizontal divider lines should be displayed between items in each + * column. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *showItemDividers; + +@end + + +/** + * Allows you to organize the numeric values in a source data column into + * buckets of a constant size. All values from HistogramRule.start to + * HistogramRule.end are placed into groups of size + * HistogramRule.interval. In addition, all values below + * HistogramRule.start are placed in one group, and all values above + * HistogramRule.end are placed in another. Only + * HistogramRule.interval is required, though if HistogramRule.start + * and HistogramRule.end are both provided, HistogramRule.start must + * be less than HistogramRule.end. For example, a pivot table showing + * average purchase amount by age that has 50+ rows: + * +-----+-------------------+ + * | Age | AVERAGE of Amount | + * +-----+-------------------+ + * | 16 | $27.13 | + * | 17 | $5.24 | + * | 18 | $20.15 | + * ... + * +-----+-------------------+ + * could be turned into a pivot table that looks like the one below by + * applying a histogram group rule with a HistogramRule.start of 25, + * an HistogramRule.interval of 20, and an HistogramRule.end + * of 65. + * +-------------+-------------------+ + * | Grouped Age | AVERAGE of Amount | + * +-------------+-------------------+ + * | < 25 | $19.34 | + * | 25-45 | $31.43 | + * | 45-65 | $35.87 | + * | > 65 | $27.55 | + * +-------------+-------------------+ + * | Grand Total | $29.12 | + * +-------------+-------------------+ + */ +@interface GTLRSheets_HistogramRule : GTLRObject + +/** + * The maximum value at which items are placed into buckets + * of constant size. Values above end are lumped into a single bucket. + * This field is optional. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *end; + +/** + * The size of the buckets that are created. Must be positive. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *interval; + +/** + * The minimum value at which items are placed into buckets + * of constant size. Values below start are lumped into a single bucket. + * This field is optional. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *start; + +@end + + +/** + * A histogram series containing the series color and data. + */ +@interface GTLRSheets_HistogramSeries : GTLRObject + +/** + * The color of the column representing this series in each bucket. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *barColor; + +/** The data for this histogram series. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *data; + +@end + + +/** + * Inserts rows or columns in a sheet at a particular index. + */ +@interface GTLRSheets_InsertDimensionRequest : GTLRObject + +/** + * Whether dimension properties should be extended from the dimensions + * before or after the newly inserted dimensions. + * True to inherit from the dimensions before (in which case the start + * index must be greater than 0), and false to inherit from the dimensions + * after. + * For example, if row index 0 has red background and row index 1 + * has a green background, then inserting 2 rows at index 1 can inherit + * either the green or red background. If `inheritFromBefore` is true, + * the two new rows will be red (because the row before the insertion point + * was red), whereas if `inheritFromBefore` is false, the two new rows will + * be green (because the row after the insertion point was green). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *inheritFromBefore; + +/** + * The dimensions to insert. Both the start and end indexes must be bounded. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * Inserts cells into a range, shifting the existing cells over or down. + */ +@interface GTLRSheets_InsertRangeRequest : GTLRObject + +/** The range to insert new cells into. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The dimension which will be shifted when inserting cells. + * If ROWS, existing cells will be shifted down. + * If COLUMNS, existing cells will be shifted right. + * + * Likely values: + * @arg @c kGTLRSheets_InsertRangeRequest_ShiftDimension_Columns Operates on + * the columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_InsertRangeRequest_ShiftDimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_InsertRangeRequest_ShiftDimension_Rows Operates on the + * rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *shiftDimension; + +@end + + +/** + * A single interpolation point on a gradient conditional format. + * These pin the gradient color scale according to the color, + * type and value chosen. + */ +@interface GTLRSheets_InterpolationPoint : GTLRObject + +/** The color this interpolation point should use. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *color; + +/** + * How the value should be interpreted. + * + * Likely values: + * @arg @c kGTLRSheets_InterpolationPoint_Type_InterpolationPointTypeUnspecified + * The default value, do not use. (Value: + * "INTERPOLATION_POINT_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_InterpolationPoint_Type_Max The interpolation point + * uses the maximum value in the + * cells over the range of the conditional format. (Value: "MAX") + * @arg @c kGTLRSheets_InterpolationPoint_Type_Min The interpolation point + * uses the minimum value in the + * cells over the range of the conditional format. (Value: "MIN") + * @arg @c kGTLRSheets_InterpolationPoint_Type_Number The interpolation point + * uses exactly the value in + * InterpolationPoint.value. (Value: "NUMBER") + * @arg @c kGTLRSheets_InterpolationPoint_Type_Percent The interpolation + * point is the given percentage over + * all the cells in the range of the conditional format. + * This is equivalent to NUMBER if the value was: + * `=(MAX(FLATTEN(range)) * (value / 100)) + * + (MIN(FLATTEN(range)) * (1 - (value / 100)))` + * (where errors in the range are ignored when flattening). (Value: + * "PERCENT") + * @arg @c kGTLRSheets_InterpolationPoint_Type_Percentile The interpolation + * point is the given percentile + * over all the cells in the range of the conditional format. + * This is equivalent to NUMBER if the value was: + * `=PERCENTILE(FLATTEN(range), value / 100)` + * (where errors in the range are ignored when flattening). (Value: + * "PERCENTILE") + */ +@property(nonatomic, copy, nullable) NSString *type; + +/** + * The value this interpolation point uses. May be a formula. + * Unused if type is MIN or + * MAX. + */ +@property(nonatomic, copy, nullable) NSString *value; + +@end + + +/** + * Settings to control how circular dependencies are resolved with iterative + * calculation. + */ +@interface GTLRSheets_IterativeCalculationSettings : GTLRObject + +/** + * When iterative calculation is enabled and successive results differ by + * less than this threshold value, the calculation rounds stop. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *convergenceThreshold; + +/** + * When iterative calculation is enabled, the maximum number of calculation + * rounds to perform. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *maxIterations; + +@end + + +/** + * Properties that describe the style of a line. + */ +@interface GTLRSheets_LineStyle : GTLRObject + +/** + * The dash type of the line. + * + * Likely values: + * @arg @c kGTLRSheets_LineStyle_Type_Custom A custom dash for a line. + * Modifying the exact custom dash style is + * currently unsupported. (Value: "CUSTOM") + * @arg @c kGTLRSheets_LineStyle_Type_Dotted A dotted line. (Value: "DOTTED") + * @arg @c kGTLRSheets_LineStyle_Type_Invisible No dash type, which is + * equivalent to a non-visible line. (Value: "INVISIBLE") + * @arg @c kGTLRSheets_LineStyle_Type_LineDashTypeUnspecified Default value, + * do not use. (Value: "LINE_DASH_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_LineStyle_Type_LongDashed A dashed line where the + * dashes have "long" length. (Value: "LONG_DASHED") + * @arg @c kGTLRSheets_LineStyle_Type_LongDashedDotted A line that alternates + * between a "long" dash and a dot. (Value: "LONG_DASHED_DOTTED") + * @arg @c kGTLRSheets_LineStyle_Type_MediumDashed A dashed line where the + * dashes have "medium" length. (Value: "MEDIUM_DASHED") + * @arg @c kGTLRSheets_LineStyle_Type_MediumDashedDotted A line that + * alternates between a "medium" dash and a dot. (Value: + * "MEDIUM_DASHED_DOTTED") + * @arg @c kGTLRSheets_LineStyle_Type_Solid A solid line. (Value: "SOLID") + */ +@property(nonatomic, copy, nullable) NSString *type; + +/** + * The thickness of the line, in px. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *width; + +@end + + +/** + * Allows you to manually organize the values in a source data column into + * buckets with names of your choosing. For example, a pivot table that + * aggregates population by state: + * +-------+-------------------+ + * | State | SUM of Population | + * +-------+-------------------+ + * | AK | 0.7 | + * | AL | 4.8 | + * | AR | 2.9 | + * ... + * +-------+-------------------+ + * could be turned into a pivot table that aggregates population by time zone + * by providing a list of groups (for example, groupName = 'Central', + * items = ['AL', 'AR', 'IA', ...]) to a manual group rule. + * Note that a similar effect could be achieved by adding a time zone column + * to the source data and adjusting the pivot table. + * +-----------+-------------------+ + * | Time Zone | SUM of Population | + * +-----------+-------------------+ + * | Central | 106.3 | + * | Eastern | 151.9 | + * | Mountain | 17.4 | + * ... + * +-----------+-------------------+ + */ +@interface GTLRSheets_ManualRule : GTLRObject + +/** + * The list of group names and the corresponding items from the source data + * that map to each group name. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ManualRuleGroup *> *groups; + +@end + + +/** + * A group name and a list of items from the source data that should be placed + * in the group with this name. + * + * @note This class supports NSFastEnumeration and indexed subscripting over + * its "items" property. + */ +@interface GTLRSheets_ManualRuleGroup : GTLRCollectionObject + +/** + * The group name, which must be a string. Each group in a given + * ManualRule must have a unique group name. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ExtendedValue *groupName; + +/** + * The items in the source data that should be placed into this group. Each + * item may be a string, number, or boolean. Items may appear in at most one + * group within a given ManualRule. Items that do not appear in any + * group will appear on their own. + * + * @note This property is used to support NSFastEnumeration and indexed + * subscripting on this class. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ExtendedValue *> *items; + +@end + + +/** + * A developer metadata entry and the data filters specified in the original + * request that matched it. + */ +@interface GTLRSheets_MatchedDeveloperMetadata : GTLRObject + +/** All filters matching the returned developer metadata. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +/** The developer metadata matching the specified filters. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadata *developerMetadata; + +@end + + +/** + * A value range that was matched by one or more data filers. + */ +@interface GTLRSheets_MatchedValueRange : GTLRObject + +/** + * The DataFilters from the request that matched the range of + * values. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +/** The values matched by the DataFilter. */ +@property(nonatomic, strong, nullable) GTLRSheets_ValueRange *valueRange; + +@end + + +/** + * Merges all cells in the range. + */ +@interface GTLRSheets_MergeCellsRequest : GTLRObject + +/** + * How the cells should be merged. + * + * Likely values: + * @arg @c kGTLRSheets_MergeCellsRequest_MergeType_MergeAll Create a single + * merge from the range (Value: "MERGE_ALL") + * @arg @c kGTLRSheets_MergeCellsRequest_MergeType_MergeColumns Create a + * merge for each column in the range (Value: "MERGE_COLUMNS") + * @arg @c kGTLRSheets_MergeCellsRequest_MergeType_MergeRows Create a merge + * for each row in the range (Value: "MERGE_ROWS") + */ +@property(nonatomic, copy, nullable) NSString *mergeType; + +/** The range of cells to merge. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +@end + + +/** + * Moves one or more rows or columns. + */ +@interface GTLRSheets_MoveDimensionRequest : GTLRObject + +/** + * The zero-based start index of where to move the source data to, + * based on the coordinates *before* the source data is removed + * from the grid. Existing data will be shifted down or right + * (depending on the dimension) to make room for the moved dimensions. + * The source dimensions are removed from the grid, so the + * the data may end up in a different index than specified. + * For example, given `A1..A5` of `0, 1, 2, 3, 4` and wanting to move + * `"1"` and `"2"` to between `"3"` and `"4"`, the source would be + * `ROWS [1..3)`,and the destination index would be `"4"` + * (the zero-based index of row 5). + * The end result would be `A1..A5` of `0, 3, 1, 2, 4`. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *destinationIndex; + +/** The source dimensions to move. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *source; + +@end + + +/** + * A named range. + */ +@interface GTLRSheets_NamedRange : GTLRObject + +/** The name of the named range. */ +@property(nonatomic, copy, nullable) NSString *name; + +/** The ID of the named range. */ +@property(nonatomic, copy, nullable) NSString *namedRangeId; + +/** The range this represents. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +@end + + +/** + * The number format of a cell. + */ +@interface GTLRSheets_NumberFormat : GTLRObject + +/** + * Pattern string used for formatting. If not set, a default pattern based on + * the user's locale will be used if necessary for the given type. + * See the [Date and Number Formats guide](/sheets/api/guides/formats) for more + * information about the supported patterns. + */ +@property(nonatomic, copy, nullable) NSString *pattern; + +/** + * The type of the number format. + * When writing, this field must be set. + * + * Likely values: + * @arg @c kGTLRSheets_NumberFormat_Type_Currency Currency formatting, e.g + * `$1,000.12` (Value: "CURRENCY") + * @arg @c kGTLRSheets_NumberFormat_Type_Date Date formatting, e.g + * `9/26/2008` (Value: "DATE") + * @arg @c kGTLRSheets_NumberFormat_Type_DateTime Date+Time formatting, e.g + * `9/26/08 15:59:00` (Value: "DATE_TIME") + * @arg @c kGTLRSheets_NumberFormat_Type_Number Number formatting, e.g, + * `1,000.12` (Value: "NUMBER") + * @arg @c kGTLRSheets_NumberFormat_Type_NumberFormatTypeUnspecified The + * number format is not specified + * and is based on the contents of the cell. + * Do not explicitly use this. (Value: "NUMBER_FORMAT_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_NumberFormat_Type_Percent Percent formatting, e.g + * `10.12%` (Value: "PERCENT") + * @arg @c kGTLRSheets_NumberFormat_Type_Scientific Scientific number + * formatting, e.g `1.01E+03` (Value: "SCIENTIFIC") + * @arg @c kGTLRSheets_NumberFormat_Type_Text Text formatting, e.g `1000.12` + * (Value: "TEXT") + * @arg @c kGTLRSheets_NumberFormat_Type_Time Time formatting, e.g `3:59:00 + * PM` (Value: "TIME") + */ +@property(nonatomic, copy, nullable) NSString *type; + +@end + + +/** + * An <a href="/chart/interactive/docs/gallery/orgchart">org chart</a>. + * Org charts require a unique set of labels in labels and may + * optionally include parent_labels and tooltips. + * parent_labels contain, for each node, the label identifying the parent + * node. tooltips contain, for each node, an optional tooltip. + * For example, to describe an OrgChart with Alice as the CEO, Bob as the + * President (reporting to Alice) and Cathy as VP of Sales (also reporting to + * Alice), have labels contain "Alice", "Bob", "Cathy", + * parent_labels contain "", "Alice", "Alice" and tooltips contain + * "CEO", "President", "VP Sales". + */ +@interface GTLRSheets_OrgChartSpec : GTLRObject + +/** + * The data containing the labels for all the nodes in the chart. Labels + * must be unique. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *labels; + +/** The color of the org chart nodes. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *nodeColor; + +/** + * The size of the org chart nodes. + * + * Likely values: + * @arg @c kGTLRSheets_OrgChartSpec_NodeSize_Large The large org chart node + * size. (Value: "LARGE") + * @arg @c kGTLRSheets_OrgChartSpec_NodeSize_Medium The medium org chart node + * size. (Value: "MEDIUM") + * @arg @c kGTLRSheets_OrgChartSpec_NodeSize_OrgChartLabelSizeUnspecified + * Default value, do not use. (Value: "ORG_CHART_LABEL_SIZE_UNSPECIFIED") + * @arg @c kGTLRSheets_OrgChartSpec_NodeSize_Small The small org chart node + * size. (Value: "SMALL") + */ +@property(nonatomic, copy, nullable) NSString *nodeSize; + +/** + * The data containing the label of the parent for the corresponding node. + * A blank value indicates that the node has no parent and is a top-level + * node. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *parentLabels; + +/** The color of the selected org chart nodes. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *selectedNodeColor; + +/** + * The data containing the tooltip for the corresponding node. A blank value + * results in no tooltip being displayed for the node. + * This field is optional. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *tooltips; + +@end + + +/** + * The location an object is overlaid on top of a grid. + */ +@interface GTLRSheets_OverlayPosition : GTLRObject + +/** The cell the object is anchored to. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridCoordinate *anchorCell; + +/** + * The height of the object, in pixels. Defaults to 371. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *heightPixels; + +/** + * The horizontal offset, in pixels, that the object is offset + * from the anchor cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *offsetXPixels; + +/** + * The vertical offset, in pixels, that the object is offset + * from the anchor cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *offsetYPixels; + +/** + * The width of the object, in pixels. Defaults to 600. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *widthPixels; + +@end + + +/** + * The amount of padding around the cell, in pixels. + * When updating padding, every field must be specified. + */ +@interface GTLRSheets_Padding : GTLRObject + +/** + * The bottom padding of the cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bottom; + +/** + * The left padding of the cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *left; + +/** + * The right padding of the cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *right; + +/** + * The top padding of the cell. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *top; + +@end + + +/** + * Inserts data into the spreadsheet starting at the specified coordinate. + */ +@interface GTLRSheets_PasteDataRequest : GTLRObject + +/** The coordinate at which the data should start being inserted. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridCoordinate *coordinate; + +/** The data to insert. */ +@property(nonatomic, copy, nullable) NSString *data; + +/** The delimiter in the data. */ +@property(nonatomic, copy, nullable) NSString *delimiter; + +/** + * True if the data is HTML. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *html; + +/** + * How the data should be pasted. + * + * Likely values: + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteConditionalFormatting Paste + * the conditional formatting rules only. (Value: + * "PASTE_CONDITIONAL_FORMATTING") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteDataValidation Paste the + * data validation only. (Value: "PASTE_DATA_VALIDATION") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteFormat Paste the format and + * data validation only. (Value: "PASTE_FORMAT") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteFormula Paste the formulas + * only. (Value: "PASTE_FORMULA") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteNoBorders Like PASTE_NORMAL + * but without borders. (Value: "PASTE_NO_BORDERS") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteNormal Paste values, + * formulas, formats, and merges. (Value: "PASTE_NORMAL") + * @arg @c kGTLRSheets_PasteDataRequest_Type_PasteValues Paste the values + * ONLY without formats, formulas, or merges. (Value: "PASTE_VALUES") + */ +@property(nonatomic, copy, nullable) NSString *type; + +@end + + +/** + * A <a href="/chart/interactive/docs/gallery/piechart">pie chart</a>. + */ +@interface GTLRSheets_PieChartSpec : GTLRObject + +/** The data that covers the domain of the pie chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *domain; + +/** + * Where the legend of the pie chart should be drawn. + * + * Likely values: + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_BottomLegend The legend is + * rendered on the bottom of the chart. (Value: "BOTTOM_LEGEND") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_LabeledLegend Each pie + * slice has a label attached to it. (Value: "LABELED_LEGEND") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_LeftLegend The legend is + * rendered on the left of the chart. (Value: "LEFT_LEGEND") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_NoLegend No legend is + * rendered. (Value: "NO_LEGEND") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_PieChartLegendPositionUnspecified + * Default value, do not use. (Value: + * "PIE_CHART_LEGEND_POSITION_UNSPECIFIED") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_RightLegend The legend is + * rendered on the right of the chart. (Value: "RIGHT_LEGEND") + * @arg @c kGTLRSheets_PieChartSpec_LegendPosition_TopLegend The legend is + * rendered on the top of the chart. (Value: "TOP_LEGEND") + */ +@property(nonatomic, copy, nullable) NSString *legendPosition; + +/** + * The size of the hole in the pie chart. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *pieHole; + +/** The data that covers the one and only series of the pie chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *series; + +/** + * True if the pie is three dimensional. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *threeDimensional; + +@end + + +/** + * Criteria for showing/hiding rows in a pivot table. + */ +@interface GTLRSheets_PivotFilterCriteria : GTLRObject + +/** Values that should be included. Values not listed here are excluded. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *visibleValues; + +@end + + +/** + * A single grouping (either row or column) in a pivot table. + */ +@interface GTLRSheets_PivotGroup : GTLRObject + +/** The group rule to apply to this row/column group. */ +@property(nonatomic, strong, nullable) GTLRSheets_PivotGroupRule *groupRule; + +/** + * The labels to use for the row/column groups which can be customized. For + * example, in the following pivot table, the row label is `Region` (which + * could be renamed to `State`) and the column label is `Product` (which + * could be renamed `Item`). Pivot tables created before December 2017 do + * not have header labels. If you'd like to add header labels to an existing + * pivot table, please delete the existing pivot table and then create a new + * pivot table with same parameters. + * +--------------+---------+-------+ + * | SUM of Units | Product | | + * | Region | Pen | Paper | + * +--------------+---------+-------+ + * | New York | 345 | 98 | + * | Oregon | 234 | 123 | + * | Tennessee | 531 | 415 | + * +--------------+---------+-------+ + * | Grand Total | 1110 | 636 | + * +--------------+---------+-------+ + */ +@property(nonatomic, copy, nullable) NSString *label; + +/** + * True if the headings in this pivot group should be repeated. + * This is only valid for row groupings and is ignored by columns. + * By default, we minimize repitition of headings by not showing higher + * level headings where they are the same. For example, even though the + * third row below corresponds to "Q1 Mar", "Q1" is not shown because + * it is redundant with previous rows. Setting repeat_headings to true + * would cause "Q1" to be repeated for "Feb" and "Mar". + * +--------------+ + * | Q1 | Jan | + * | | Feb | + * | | Mar | + * +--------+-----+ + * | Q1 Total | + * +--------------+ + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *repeatHeadings; + +/** + * True if the pivot table should include the totals for this grouping. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *showTotals; + +/** + * The order the values in this group should be sorted. + * + * Likely values: + * @arg @c kGTLRSheets_PivotGroup_SortOrder_Ascending Sort ascending. (Value: + * "ASCENDING") + * @arg @c kGTLRSheets_PivotGroup_SortOrder_Descending Sort descending. + * (Value: "DESCENDING") + * @arg @c kGTLRSheets_PivotGroup_SortOrder_SortOrderUnspecified Default + * value, do not use this. (Value: "SORT_ORDER_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *sortOrder; + +/** + * The column offset of the source range that this grouping is based on. + * For example, if the source was `C10:E15`, a `sourceColumnOffset` of `0` + * means this group refers to column `C`, whereas the offset `1` would refer + * to column `D`. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sourceColumnOffset; + +/** + * The bucket of the opposite pivot group to sort by. + * If not specified, sorting is alphabetical by this group's values. + */ +@property(nonatomic, strong, nullable) GTLRSheets_PivotGroupSortValueBucket *valueBucket; + +/** Metadata about values in the grouping. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_PivotGroupValueMetadata *> *valueMetadata; + +@end + + +/** + * An optional setting on a PivotGroup that defines buckets for the values + * in the source data column rather than breaking out each individual value. + * Only one PivotGroup with a group rule may be added for each column in + * the source data, though on any given column you may add both a + * PivotGroup that has a rule and a PivotGroup that does not. + */ +@interface GTLRSheets_PivotGroupRule : GTLRObject + +/** A DateTimeRule. */ +@property(nonatomic, strong, nullable) GTLRSheets_DateTimeRule *dateTimeRule; + +/** A HistogramRule. */ +@property(nonatomic, strong, nullable) GTLRSheets_HistogramRule *histogramRule; + +/** A ManualRule. */ +@property(nonatomic, strong, nullable) GTLRSheets_ManualRule *manualRule; + +@end + + +/** + * Information about which values in a pivot group should be used for sorting. + */ +@interface GTLRSheets_PivotGroupSortValueBucket : GTLRObject + +/** + * Determines the bucket from which values are chosen to sort. + * For example, in a pivot table with one row group & two column groups, + * the row group can list up to two values. The first value corresponds + * to a value within the first column group, and the second value + * corresponds to a value in the second column group. If no values + * are listed, this would indicate that the row should be sorted according + * to the "Grand Total" over the column groups. If a single value is listed, + * this would correspond to using the "Total" of that bucket. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ExtendedValue *> *buckets; + +/** + * The offset in the PivotTable.values list which the values in this + * grouping should be sorted by. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *valuesIndex; + +@end + + +/** + * Metadata about a value in a pivot grouping. + */ +@interface GTLRSheets_PivotGroupValueMetadata : GTLRObject + +/** + * True if the data corresponding to the value is collapsed. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *collapsed; + +/** + * The calculated value the metadata corresponds to. + * (Note that formulaValue is not valid, + * because the values will be calculated.) + */ +@property(nonatomic, strong, nullable) GTLRSheets_ExtendedValue *value; + +@end + + +/** + * A pivot table. + */ +@interface GTLRSheets_PivotTable : GTLRObject + +/** Each column grouping in the pivot table. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_PivotGroup *> *columns; + +/** + * An optional mapping of filters per source column offset. + * The filters are applied before aggregating data into the pivot table. + * The map's key is the column offset of the source range that you want to + * filter, and the value is the criteria for that column. + * For example, if the source was `C10:E15`, a key of `0` will have the filter + * for column `C`, whereas the key `1` is for column `D`. + */ +@property(nonatomic, strong, nullable) GTLRSheets_PivotTable_Criteria *criteria; + +/** Each row grouping in the pivot table. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_PivotGroup *> *rows; + +/** The range the pivot table is reading data from. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *source; + +/** + * Whether values should be listed horizontally (as columns) + * or vertically (as rows). + * + * Likely values: + * @arg @c kGTLRSheets_PivotTable_ValueLayout_Horizontal Values are laid out + * horizontally (as columns). (Value: "HORIZONTAL") + * @arg @c kGTLRSheets_PivotTable_ValueLayout_Vertical Values are laid out + * vertically (as rows). (Value: "VERTICAL") + */ +@property(nonatomic, copy, nullable) NSString *valueLayout; + +/** A list of values to include in the pivot table. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_PivotValue *> *values; + +@end + + +/** + * An optional mapping of filters per source column offset. + * The filters are applied before aggregating data into the pivot table. + * The map's key is the column offset of the source range that you want to + * filter, and the value is the criteria for that column. + * For example, if the source was `C10:E15`, a key of `0` will have the filter + * for column `C`, whereas the key `1` is for column `D`. + * + * @note This class is documented as having more properties of + * GTLRSheets_PivotFilterCriteria. Use @c -additionalJSONKeys and @c + * -additionalPropertyForName: to get the list of properties and then + * fetch them; or @c -additionalProperties to fetch them all at once. + */ +@interface GTLRSheets_PivotTable_Criteria : GTLRObject +@end + + +/** + * The definition of how a value in a pivot table should be calculated. + */ +@interface GTLRSheets_PivotValue : GTLRObject + +/** + * If specified, indicates that pivot values should be displayed as + * the result of a calculation with another pivot value. For example, if + * calculated_display_type is specified as PERCENT_OF_GRAND_TOTAL, all the + * pivot values are displayed as the percentage of the grand total. In + * the Sheets UI, this is referred to as "Show As" in the value section of a + * pivot table. + * + * Likely values: + * @arg @c kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfColumnTotal + * Shows the pivot values as percentage of the column total values. + * (Value: "PERCENT_OF_COLUMN_TOTAL") + * @arg @c kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfGrandTotal + * Shows the pivot values as percentage of the grand total values. + * (Value: "PERCENT_OF_GRAND_TOTAL") + * @arg @c kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfRowTotal + * Shows the pivot values as percentage of the row total values. (Value: + * "PERCENT_OF_ROW_TOTAL") + * @arg @c kGTLRSheets_PivotValue_CalculatedDisplayType_PivotValueCalculatedDisplayTypeUnspecified + * Default value, do not use. (Value: + * "PIVOT_VALUE_CALCULATED_DISPLAY_TYPE_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *calculatedDisplayType; + +/** + * A custom formula to calculate the value. The formula must start + * with an `=` character. + */ +@property(nonatomic, copy, nullable) NSString *formula; + +/** A name to use for the value. */ +@property(nonatomic, copy, nullable) NSString *name; + +/** + * The column offset of the source range that this value reads from. + * For example, if the source was `C10:E15`, a `sourceColumnOffset` of `0` + * means this value refers to column `C`, whereas the offset `1` would + * refer to column `D`. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sourceColumnOffset; + +/** + * A function to summarize the value. + * If formula is set, the only supported values are + * SUM and + * CUSTOM. + * If sourceColumnOffset is set, then `CUSTOM` + * is not supported. + * + * Likely values: + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Average Corresponds to + * the `AVERAGE` function. (Value: "AVERAGE") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Count Corresponds to the + * `COUNT` function. (Value: "COUNT") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Counta Corresponds to the + * `COUNTA` function. (Value: "COUNTA") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Countunique Corresponds + * to the `COUNTUNIQUE` function. (Value: "COUNTUNIQUE") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Custom Indicates the + * formula should be used as-is. + * Only valid if PivotValue.formula was set. (Value: "CUSTOM") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Max Corresponds to the + * `MAX` function. (Value: "MAX") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Median Corresponds to the + * `MEDIAN` function. (Value: "MEDIAN") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Min Corresponds to the + * `MIN` function. (Value: "MIN") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_PivotStandardValueFunctionUnspecified + * The default, do not use. (Value: + * "PIVOT_STANDARD_VALUE_FUNCTION_UNSPECIFIED") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Product Corresponds to + * the `PRODUCT` function. (Value: "PRODUCT") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Stdev Corresponds to the + * `STDEV` function. (Value: "STDEV") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Stdevp Corresponds to the + * `STDEVP` function. (Value: "STDEVP") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Sum Corresponds to the + * `SUM` function. (Value: "SUM") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Var Corresponds to the + * `VAR` function. (Value: "VAR") + * @arg @c kGTLRSheets_PivotValue_SummarizeFunction_Varp Corresponds to the + * `VARP` function. (Value: "VARP") + */ +@property(nonatomic, copy, nullable) NSString *summarizeFunction; + +@end + + +/** + * A protected range. + */ +@interface GTLRSheets_ProtectedRange : GTLRObject + +/** + * The description of this protected range. + * + * Remapped to 'descriptionProperty' to avoid NSObject's 'description'. + */ +@property(nonatomic, copy, nullable) NSString *descriptionProperty; + +/** + * The users and groups with edit access to the protected range. + * This field is only visible to users with edit access to the protected + * range and the document. + * Editors are not supported with warning_only protection. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Editors *editors; + +/** + * The named range this protected range is backed by, if any. + * When writing, only one of range or named_range_id + * may be set. + */ +@property(nonatomic, copy, nullable) NSString *namedRangeId; + +/** + * The ID of the protected range. + * This field is read-only. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *protectedRangeId; + +/** + * The range that is being protected. + * The range may be fully unbounded, in which case this is considered + * a protected sheet. + * When writing, only one of range or named_range_id + * may be set. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * True if the user who requested this protected range can edit the + * protected area. + * This field is read-only. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *requestingUserCanEdit; + +/** + * The list of unprotected ranges within a protected sheet. + * Unprotected ranges are only supported on protected sheets. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_GridRange *> *unprotectedRanges; + +/** + * True if this protected range will show a warning when editing. + * Warning-based protection means that every user can edit data in the + * protected range, except editing will prompt a warning asking the user + * to confirm the edit. + * When writing: if this field is true, then editors is ignored. + * Additionally, if this field is changed from true to false and the + * `editors` field is not set (nor included in the field mask), then + * the editors will be set to all the editors in the document. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *warningOnly; + +@end + + +/** + * Randomizes the order of the rows in a range. + */ +@interface GTLRSheets_RandomizeRangeRequest : GTLRObject + +/** The range to randomize. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +@end + + +/** + * Updates all cells in the range to the values in the given Cell object. + * Only the fields listed in the fields field are updated; others are + * unchanged. + * If writing a cell with a formula, the formula's ranges will automatically + * increment for each field in the range. + * For example, if writing a cell with formula `=A1` into range B2:C4, + * B2 would be `=A1`, B3 would be `=A2`, B4 would be `=A3`, + * C2 would be `=B1`, C3 would be `=B2`, C4 would be `=B3`. + * To keep the formula's ranges static, use the `$` indicator. + * For example, use the formula `=$A$1` to prevent both the row and the + * column from incrementing. + */ +@interface GTLRSheets_RepeatCellRequest : GTLRObject + +/** The data to write. */ +@property(nonatomic, strong, nullable) GTLRSheets_CellData *cell; + +/** + * The fields that should be updated. At least one field must be specified. + * The root `cell` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The range to repeat the cell in. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +@end + + +/** + * A single kind of update to apply to a spreadsheet. + */ +@interface GTLRSheets_Request : GTLRObject + +/** Adds a new banded range */ +@property(nonatomic, strong, nullable) GTLRSheets_AddBandingRequest *addBanding; + +/** Adds a chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddChartRequest *addChart; + +/** Adds a new conditional format rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddConditionalFormatRuleRequest *addConditionalFormatRule; + +/** Creates a group over the specified range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddDimensionGroupRequest *addDimensionGroup; + +/** Adds a filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddFilterViewRequest *addFilterView; + +/** Adds a named range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddNamedRangeRequest *addNamedRange; + +/** Adds a protected range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddProtectedRangeRequest *addProtectedRange; + +/** Adds a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddSheetRequest *addSheet; + +/** Appends cells after the last row with data in a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_AppendCellsRequest *appendCells; + +/** Appends dimensions to the end of a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_AppendDimensionRequest *appendDimension; + +/** Automatically fills in more data based on existing data. */ +@property(nonatomic, strong, nullable) GTLRSheets_AutoFillRequest *autoFill; + +/** + * Automatically resizes one or more dimensions based on the contents + * of the cells in that dimension. + */ +@property(nonatomic, strong, nullable) GTLRSheets_AutoResizeDimensionsRequest *autoResizeDimensions; + +/** Clears the basic filter on a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_ClearBasicFilterRequest *clearBasicFilter; + +/** Copies data from one area and pastes it to another. */ +@property(nonatomic, strong, nullable) GTLRSheets_CopyPasteRequest *copyPaste NS_RETURNS_NOT_RETAINED; + +/** Creates new developer metadata */ +@property(nonatomic, strong, nullable) GTLRSheets_CreateDeveloperMetadataRequest *createDeveloperMetadata; + +/** Cuts data from one area and pastes it to another. */ +@property(nonatomic, strong, nullable) GTLRSheets_CutPasteRequest *cutPaste; + +/** Removes a banded range */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteBandingRequest *deleteBanding; + +/** Deletes an existing conditional format rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteConditionalFormatRuleRequest *deleteConditionalFormatRule; + +/** Deletes developer metadata */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteDeveloperMetadataRequest *deleteDeveloperMetadata; + +/** Deletes rows or columns in a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteDimensionRequest *deleteDimension; + +/** Deletes a group over the specified range. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteDimensionGroupRequest *deleteDimensionGroup; + +/** Deletes an embedded object (e.g, chart, image) in a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteEmbeddedObjectRequest *deleteEmbeddedObject; + +/** Deletes a filter view from a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteFilterViewRequest *deleteFilterView; + +/** Deletes a named range. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteNamedRangeRequest *deleteNamedRange; + +/** Deletes a protected range. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteProtectedRangeRequest *deleteProtectedRange; + +/** Deletes a range of cells from a sheet, shifting the remaining cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteRangeRequest *deleteRange; + +/** Deletes a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteSheetRequest *deleteSheet; + +/** Duplicates a filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_DuplicateFilterViewRequest *duplicateFilterView; + +/** Duplicates a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DuplicateSheetRequest *duplicateSheet; + +/** Finds and replaces occurrences of some text with other text. */ +@property(nonatomic, strong, nullable) GTLRSheets_FindReplaceRequest *findReplace; + +/** Inserts new rows or columns in a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_InsertDimensionRequest *insertDimension; + +/** Inserts new cells in a sheet, shifting the existing cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_InsertRangeRequest *insertRange; + +/** Merges cells together. */ +@property(nonatomic, strong, nullable) GTLRSheets_MergeCellsRequest *mergeCells; + +/** Moves rows or columns to another location in a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_MoveDimensionRequest *moveDimension; + +/** Pastes data (HTML or delimited) into a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_PasteDataRequest *pasteData; + +/** Randomizes the order of the rows in a range. */ +@property(nonatomic, strong, nullable) GTLRSheets_RandomizeRangeRequest *randomizeRange; + +/** Repeats a single cell across a range. */ +@property(nonatomic, strong, nullable) GTLRSheets_RepeatCellRequest *repeatCell; + +/** Sets the basic filter on a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_SetBasicFilterRequest *setBasicFilter; + +/** Sets data validation for one or more cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_SetDataValidationRequest *setDataValidation; + +/** Sorts data in a range. */ +@property(nonatomic, strong, nullable) GTLRSheets_SortRangeRequest *sortRange; + +/** Converts a column of text into many columns of text. */ +@property(nonatomic, strong, nullable) GTLRSheets_TextToColumnsRequest *textToColumns; + +/** Unmerges merged cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_UnmergeCellsRequest *unmergeCells; + +/** Updates a banded range */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateBandingRequest *updateBanding; + +/** Updates the borders in a range of cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateBordersRequest *updateBorders; + +/** Updates many cells at once. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateCellsRequest *updateCells; + +/** Updates a chart's specifications. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateChartSpecRequest *updateChartSpec; + +/** Updates an existing conditional format rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateConditionalFormatRuleRequest *updateConditionalFormatRule; + +/** Updates an existing developer metadata entry */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateDeveloperMetadataRequest *updateDeveloperMetadata; + +/** Updates the state of the specified group. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateDimensionGroupRequest *updateDimensionGroup; + +/** Updates dimensions' properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateDimensionPropertiesRequest *updateDimensionProperties; + +/** Updates an embedded object's (e.g. chart, image) position. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateEmbeddedObjectPositionRequest *updateEmbeddedObjectPosition; + +/** Updates the properties of a filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateFilterViewRequest *updateFilterView; + +/** Updates a named range. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateNamedRangeRequest *updateNamedRange; + +/** Updates a protected range. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateProtectedRangeRequest *updateProtectedRange; + +/** Updates a sheet's properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateSheetPropertiesRequest *updateSheetProperties; + +/** Updates the spreadsheet's properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateSpreadsheetPropertiesRequest *updateSpreadsheetProperties; + +@end + + +/** + * A single response from an update. + */ +@interface GTLRSheets_Response : GTLRObject + +/** A reply from adding a banded range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddBandingResponse *addBanding; + +/** A reply from adding a chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddChartResponse *addChart; + +/** A reply from adding a dimension group. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddDimensionGroupResponse *addDimensionGroup; + +/** A reply from adding a filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddFilterViewResponse *addFilterView; + +/** A reply from adding a named range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddNamedRangeResponse *addNamedRange; + +/** A reply from adding a protected range. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddProtectedRangeResponse *addProtectedRange; + +/** A reply from adding a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_AddSheetResponse *addSheet; + +/** A reply from creating a developer metadata entry. */ +@property(nonatomic, strong, nullable) GTLRSheets_CreateDeveloperMetadataResponse *createDeveloperMetadata; + +/** A reply from deleting a conditional format rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteConditionalFormatRuleResponse *deleteConditionalFormatRule; + +/** A reply from deleting a developer metadata entry. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteDeveloperMetadataResponse *deleteDeveloperMetadata; + +/** A reply from deleting a dimension group. */ +@property(nonatomic, strong, nullable) GTLRSheets_DeleteDimensionGroupResponse *deleteDimensionGroup; + +/** A reply from duplicating a filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_DuplicateFilterViewResponse *duplicateFilterView; + +/** A reply from duplicating a sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_DuplicateSheetResponse *duplicateSheet; + +/** A reply from doing a find/replace. */ +@property(nonatomic, strong, nullable) GTLRSheets_FindReplaceResponse *findReplace; + +/** A reply from updating a conditional format rule. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateConditionalFormatRuleResponse *updateConditionalFormatRule; + +/** A reply from updating a developer metadata entry. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateDeveloperMetadataResponse *updateDeveloperMetadata; + +/** A reply from updating an embedded object's position. */ +@property(nonatomic, strong, nullable) GTLRSheets_UpdateEmbeddedObjectPositionResponse *updateEmbeddedObjectPosition; + +@end + + +/** + * Data about each cell in a row. + */ +@interface GTLRSheets_RowData : GTLRObject + +/** The values in the row, one per column. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_CellData *> *values; + +@end + + +/** + * A request to retrieve all developer metadata matching the set of specified + * criteria. + */ +@interface GTLRSheets_SearchDeveloperMetadataRequest : GTLRObject + +/** + * The data filters describing the criteria used to determine which + * DeveloperMetadata entries to return. DeveloperMetadata matching any of the + * specified filters will be included in the response. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +@end + + +/** + * A reply to a developer metadata search request. + */ +@interface GTLRSheets_SearchDeveloperMetadataResponse : GTLRObject + +/** The metadata matching the criteria of the search request. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_MatchedDeveloperMetadata *> *matchedDeveloperMetadata; + +@end + + +/** + * Sets the basic filter associated with a sheet. + */ +@interface GTLRSheets_SetBasicFilterRequest : GTLRObject + +/** The filter to set. */ +@property(nonatomic, strong, nullable) GTLRSheets_BasicFilter *filter; + +@end + + +/** + * Sets a data validation rule to every cell in the range. + * To clear validation in a range, call this with no rule specified. + */ +@interface GTLRSheets_SetDataValidationRequest : GTLRObject + +/** The range the data validation rule should apply to. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The data validation rule to set on each cell in the range, + * or empty to clear the data validation in the range. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DataValidationRule *rule; + +@end + + +/** + * A sheet in a spreadsheet. + */ +@interface GTLRSheets_Sheet : GTLRObject + +/** The banded (alternating colors) ranges on this sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_BandedRange *> *bandedRanges; + +/** The filter on this sheet, if any. */ +@property(nonatomic, strong, nullable) GTLRSheets_BasicFilter *basicFilter; + +/** The specifications of every chart on this sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_EmbeddedChart *> *charts; + +/** + * All column groups on this sheet, ordered by increasing range start index, + * then by group depth. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionGroup *> *columnGroups; + +/** The conditional format rules in this sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ConditionalFormatRule *> *conditionalFormats; + +/** + * Data in the grid, if this is a grid sheet. + * The number of GridData objects returned is dependent on the number of + * ranges requested on this sheet. For example, if this is representing + * `Sheet1`, and the spreadsheet was requested with ranges + * `Sheet1!A1:C10` and `Sheet1!D15:E20`, then the first GridData will have a + * startRow/startColumn of `0`, + * while the second one will have `startRow 14` (zero-based row 15), + * and `startColumn 3` (zero-based column D). + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_GridData *> *data; + +/** The developer metadata associated with a sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DeveloperMetadata *> *developerMetadata; + +/** The filter views in this sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_FilterView *> *filterViews; + +/** The ranges that are merged together. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_GridRange *> *merges; + +/** The properties of the sheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_SheetProperties *properties; + +/** The protected ranges in this sheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_ProtectedRange *> *protectedRanges; + +/** + * All row groups on this sheet, ordered by increasing range start index, then + * by group depth. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DimensionGroup *> *rowGroups; + +@end + + +/** + * Properties of a sheet. + */ +@interface GTLRSheets_SheetProperties : GTLRObject + +/** + * Additional properties of the sheet if this sheet is a grid. + * (If the sheet is an object sheet, containing a chart or image, then + * this field will be absent.) + * When writing it is an error to set any grid properties on non-grid sheets. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridProperties *gridProperties; + +/** + * True if the sheet is hidden in the UI, false if it's visible. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hidden; + +/** + * The index of the sheet within the spreadsheet. + * When adding or updating sheet properties, if this field + * is excluded then the sheet is added or moved to the end + * of the sheet list. When updating sheet indices or inserting + * sheets, movement is considered in "before the move" indexes. + * For example, if there were 3 sheets (S1, S2, S3) in order to + * move S1 ahead of S2 the index would have to be set to 2. A sheet + * index update request is ignored if the requested index is + * identical to the sheets current index or if the requested new + * index is equal to the current sheet index + 1. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *index; + +/** + * True if the sheet is an RTL sheet instead of an LTR sheet. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *rightToLeft; + +/** + * The ID of the sheet. Must be non-negative. + * This field cannot be changed once set. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +/** + * The type of sheet. Defaults to GRID. + * This field cannot be changed once set. + * + * Likely values: + * @arg @c kGTLRSheets_SheetProperties_SheetType_Grid The sheet is a grid. + * (Value: "GRID") + * @arg @c kGTLRSheets_SheetProperties_SheetType_Object The sheet has no grid + * and instead has an object like a chart or image. (Value: "OBJECT") + * @arg @c kGTLRSheets_SheetProperties_SheetType_SheetTypeUnspecified Default + * value, do not use. (Value: "SHEET_TYPE_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *sheetType; + +/** The color of the tab in the UI. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *tabColor; + +/** The name of the sheet. */ +@property(nonatomic, copy, nullable) NSString *title; + +@end + + +/** + * Sorts data in rows based on a sort order per column. + */ +@interface GTLRSheets_SortRangeRequest : GTLRObject + +/** The range to sort. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** + * The sort order per column. Later specifications are used when values + * are equal in the earlier specifications. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_SortSpec *> *sortSpecs; + +@end + + +/** + * A sort order associated with a specific column or row. + */ +@interface GTLRSheets_SortSpec : GTLRObject + +/** + * The dimension the sort should be applied to. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *dimensionIndex; + +/** + * The order data should be sorted. + * + * Likely values: + * @arg @c kGTLRSheets_SortSpec_SortOrder_Ascending Sort ascending. (Value: + * "ASCENDING") + * @arg @c kGTLRSheets_SortSpec_SortOrder_Descending Sort descending. (Value: + * "DESCENDING") + * @arg @c kGTLRSheets_SortSpec_SortOrder_SortOrderUnspecified Default value, + * do not use this. (Value: "SORT_ORDER_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *sortOrder; + +@end + + +/** + * A combination of a source range and how to extend that source. + */ +@interface GTLRSheets_SourceAndDestination : GTLRObject + +/** + * The dimension that data should be filled into. + * + * Likely values: + * @arg @c kGTLRSheets_SourceAndDestination_Dimension_Columns Operates on the + * columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_SourceAndDestination_Dimension_DimensionUnspecified + * The default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_SourceAndDestination_Dimension_Rows Operates on the + * rows of a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *dimension; + +/** + * The number of rows or columns that data should be filled into. + * Positive numbers expand beyond the last row or last column + * of the source. Negative numbers expand before the first row + * or first column of the source. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *fillLength; + +/** The location of the data to use as the source of the autofill. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *source; + +@end + + +/** + * Resource that represents a spreadsheet. + */ +@interface GTLRSheets_Spreadsheet : GTLRObject + +/** The developer metadata associated with a spreadsheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DeveloperMetadata *> *developerMetadata; + +/** The named ranges defined in a spreadsheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_NamedRange *> *namedRanges; + +/** Overall properties of a spreadsheet. */ +@property(nonatomic, strong, nullable) GTLRSheets_SpreadsheetProperties *properties; + +/** The sheets that are part of a spreadsheet. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_Sheet *> *sheets; + +/** + * The ID of the spreadsheet. + * This field is read-only. + */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The url of the spreadsheet. + * This field is read-only. + */ +@property(nonatomic, copy, nullable) NSString *spreadsheetUrl; + +@end + + +/** + * Properties of a spreadsheet. + */ +@interface GTLRSheets_SpreadsheetProperties : GTLRObject + +/** + * The amount of time to wait before volatile functions are recalculated. + * + * Likely values: + * @arg @c kGTLRSheets_SpreadsheetProperties_AutoRecalc_Hour Volatile + * functions are updated on every change and hourly. (Value: "HOUR") + * @arg @c kGTLRSheets_SpreadsheetProperties_AutoRecalc_Minute Volatile + * functions are updated on every change and every minute. (Value: + * "MINUTE") + * @arg @c kGTLRSheets_SpreadsheetProperties_AutoRecalc_OnChange Volatile + * functions are updated on every change. (Value: "ON_CHANGE") + * @arg @c kGTLRSheets_SpreadsheetProperties_AutoRecalc_RecalculationIntervalUnspecified + * Default value. This value must not be used. (Value: + * "RECALCULATION_INTERVAL_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *autoRecalc; + +/** + * The default format of all cells in the spreadsheet. + * CellData.effectiveFormat will not be set if + * the cell's format is equal to this default format. This field is read-only. + */ +@property(nonatomic, strong, nullable) GTLRSheets_CellFormat *defaultFormat; + +/** + * Determines whether and how circular references are resolved with iterative + * calculation. Absence of this field means that circular references will + * result in calculation errors. + */ +@property(nonatomic, strong, nullable) GTLRSheets_IterativeCalculationSettings *iterativeCalculationSettings; + +/** + * The locale of the spreadsheet in one of the following formats: + * * an ISO 639-1 language code such as `en` + * * an ISO 639-2 language code such as `fil`, if no 639-1 code exists + * * a combination of the ISO language code and country code, such as `en_US` + * Note: when updating this field, not all locales/languages are supported. + */ +@property(nonatomic, copy, nullable) NSString *locale; + +/** + * The time zone of the spreadsheet, in CLDR format such as + * `America/New_York`. If the time zone isn't recognized, this may + * be a custom time zone such as `GMT-07:00`. + */ +@property(nonatomic, copy, nullable) NSString *timeZone; + +/** The title of the spreadsheet. */ +@property(nonatomic, copy, nullable) NSString *title; + +@end + + +/** + * The format of a run of text in a cell. + * Absent values indicate that the field isn't specified. + */ +@interface GTLRSheets_TextFormat : GTLRObject + +/** + * True if the text is bold. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *bold; + +/** The font family. */ +@property(nonatomic, copy, nullable) NSString *fontFamily; + +/** + * The size of the font. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *fontSize; + +/** The foreground color of the text. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *foregroundColor; + +/** + * True if the text is italicized. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *italic; + +/** + * True if the text has a strikethrough. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *strikethrough; + +/** + * True if the text is underlined. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *underline; + +@end + + +/** + * A run of a text format. The format of this run continues until the start + * index of the next run. + * When updating, all fields must be set. + */ +@interface GTLRSheets_TextFormatRun : GTLRObject + +/** The format of this run. Absent values inherit the cell's format. */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *format; + +/** + * The character index where this run starts. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *startIndex; + +@end + + +/** + * Position settings for text. + */ +@interface GTLRSheets_TextPosition : GTLRObject + +/** + * Horizontal alignment setting for the piece of text. + * + * Likely values: + * @arg @c kGTLRSheets_TextPosition_HorizontalAlignment_Center The text is + * explicitly aligned to the center of the cell. (Value: "CENTER") + * @arg @c kGTLRSheets_TextPosition_HorizontalAlignment_HorizontalAlignUnspecified + * The horizontal alignment is not specified. Do not use this. (Value: + * "HORIZONTAL_ALIGN_UNSPECIFIED") + * @arg @c kGTLRSheets_TextPosition_HorizontalAlignment_Left The text is + * explicitly aligned to the left of the cell. (Value: "LEFT") + * @arg @c kGTLRSheets_TextPosition_HorizontalAlignment_Right The text is + * explicitly aligned to the right of the cell. (Value: "RIGHT") + */ +@property(nonatomic, copy, nullable) NSString *horizontalAlignment; + +@end + + +/** + * The rotation applied to text in a cell. + */ +@interface GTLRSheets_TextRotation : GTLRObject + +/** + * The angle between the standard orientation and the desired orientation. + * Measured in degrees. Valid values are between -90 and 90. Positive + * angles are angled upwards, negative are angled downwards. + * Note: For LTR text direction positive angles are in the counterclockwise + * direction, whereas for RTL they are in the clockwise direction + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *angle; + +/** + * If true, text reads top to bottom, but the orientation of individual + * characters is unchanged. + * For example: + * | V | + * | e | + * | r | + * | t | + * | i | + * | c | + * | a | + * | l | + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *vertical; + +@end + + +/** + * Splits a column of text into multiple columns, + * based on a delimiter in each cell. + */ +@interface GTLRSheets_TextToColumnsRequest : GTLRObject + +/** + * The delimiter to use. Used only if delimiterType is + * CUSTOM. + */ +@property(nonatomic, copy, nullable) NSString *delimiter; + +/** + * The delimiter type to use. + * + * Likely values: + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Autodetect + * Automatically detect columns. (Value: "AUTODETECT") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Comma "," (Value: + * "COMMA") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Custom A custom + * value as defined in delimiter. (Value: "CUSTOM") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_DelimiterTypeUnspecified + * Default value. This value must not be used. (Value: + * "DELIMITER_TYPE_UNSPECIFIED") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Period "." (Value: + * "PERIOD") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Semicolon ";" + * (Value: "SEMICOLON") + * @arg @c kGTLRSheets_TextToColumnsRequest_DelimiterType_Space " " (Value: + * "SPACE") + */ +@property(nonatomic, copy, nullable) NSString *delimiterType; + +/** The source data range. This must span exactly one column. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *source; + +@end + + +/** + * A color scale for a treemap chart. + */ +@interface GTLRSheets_TreemapChartColorScale : GTLRObject + +/** + * The background color for cells with a color value greater than or equal + * to maxValue. Defaults to #109618 if not + * specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *maxValueColor; + +/** + * The background color for cells with a color value at the midpoint between + * minValue and + * maxValue. Defaults to #efe6dc if not + * specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *midValueColor; + +/** + * The background color for cells with a color value less than or equal to + * minValue. Defaults to #dc3912 if not + * specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *minValueColor; + +/** + * The background color for cells that have no color data associated with + * them. Defaults to #000000 if not specified. + */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *noDataColor; + +@end + + +/** + * A <a href="/chart/interactive/docs/gallery/treemap">Treemap chart</a>. + */ +@interface GTLRSheets_TreemapChartSpec : GTLRObject + +/** + * The data that determines the background color of each treemap data cell. + * This field is optional. If not specified, size_data is used to + * determine background colors. If specified, the data is expected to be + * numeric. color_scale will determine how the values in this data map to + * data cell background colors. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *colorData; + +/** + * The color scale for data cells in the treemap chart. Data cells are + * assigned colors based on their color values. These color values come from + * color_data, or from size_data if color_data is not specified. + * Cells with color values less than or equal to min_value will + * have minValueColor as their + * background color. Cells with color values greater than or equal to + * max_value will have + * maxValueColor as their background + * color. Cells with color values between min_value and max_value will + * have background colors on a gradient between + * minValueColor and + * maxValueColor, the midpoint of + * the gradient being midValueColor. + * Cells with missing or non-numeric color values will have + * noDataColor as their background + * color. + */ +@property(nonatomic, strong, nullable) GTLRSheets_TreemapChartColorScale *colorScale; + +/** The background color for header cells. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *headerColor; + +/** + * True to hide tooltips. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hideTooltips; + +/** + * The number of additional data levels beyond the labeled levels to be shown + * on the treemap chart. These levels are not interactive and are shown + * without their labels. Defaults to 0 if not specified. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hintedLevels; + +/** The data that contains the treemap cell labels. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *labels; + +/** + * The number of data levels to show on the treemap chart. These levels are + * interactive and are shown with their labels. Defaults to 2 if not + * specified. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *levels; + +/** + * The maximum possible data value. Cells with values greater than this will + * have the same color as cells with this value. If not specified, defaults + * to the actual maximum value from color_data, or the maximum value from + * size_data if color_data is not specified. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *maxValue; + +/** + * The minimum possible data value. Cells with values less than this will + * have the same color as cells with this value. If not specified, defaults + * to the actual minimum value from color_data, or the minimum value from + * size_data if color_data is not specified. + * + * Uses NSNumber of doubleValue. + */ +@property(nonatomic, strong, nullable) NSNumber *minValue; + +/** The data the contains the treemap cells' parent labels. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *parentLabels; + +/** + * The data that determines the size of each treemap data cell. This data is + * expected to be numeric. The cells corresponding to non-numeric or missing + * data will not be rendered. If color_data is not specified, this data + * is used to determine data cell background colors as well. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *sizeData; + +/** The text format for all labels on the chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_TextFormat *textFormat; + +@end + + +/** + * Unmerges cells in the given range. + */ +@interface GTLRSheets_UnmergeCellsRequest : GTLRObject + +/** + * The range within which all cells should be unmerged. + * If the range spans multiple merges, all will be unmerged. + * The range must not partially span any merge. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +@end + + +/** + * Updates properties of the supplied banded range. + */ +@interface GTLRSheets_UpdateBandingRequest : GTLRObject + +/** The banded range to update with the new properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_BandedRange *bandedRange; + +/** + * The fields that should be updated. At least one field must be specified. + * The root `bandedRange` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +@end + + +/** + * Updates the borders of a range. + * If a field is not set in the request, that means the border remains as-is. + * For example, with two subsequent UpdateBordersRequest: + * 1. range: A1:A5 `{ top: RED, bottom: WHITE }` + * 2. range: A1:A5 `{ left: BLUE }` + * That would result in A1:A5 having a borders of + * `{ top: RED, bottom: WHITE, left: BLUE }`. + * If you want to clear a border, explicitly set the style to + * NONE. + */ +@interface GTLRSheets_UpdateBordersRequest : GTLRObject + +/** The border to put at the bottom of the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *bottom; + +/** The horizontal border to put within the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *innerHorizontal; + +/** The vertical border to put within the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *innerVertical; + +/** The border to put at the left of the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *left; + +/** The range whose borders should be updated. */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** The border to put at the right of the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *right; + +/** The border to put at the top of the range. */ +@property(nonatomic, strong, nullable) GTLRSheets_Border *top; + +@end + + +/** + * Updates all cells in a range with new data. + */ +@interface GTLRSheets_UpdateCellsRequest : GTLRObject + +/** + * The fields of CellData that should be updated. + * At least one field must be specified. + * The root is the CellData; 'row.values.' should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** + * The range to write data to. + * If the data in rows does not cover the entire requested range, + * the fields matching those set in fields will be cleared. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridRange *range; + +/** The data to write. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_RowData *> *rows; + +/** + * The coordinate to start writing data at. + * Any number of rows and columns (including a different number of + * columns per row) may be written. + */ +@property(nonatomic, strong, nullable) GTLRSheets_GridCoordinate *start; + +@end + + +/** + * Updates a chart's specifications. + * (This does not move or resize a chart. To move or resize a chart, use + * UpdateEmbeddedObjectPositionRequest.) + */ +@interface GTLRSheets_UpdateChartSpecRequest : GTLRObject + +/** + * The ID of the chart to update. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *chartId; + +/** The specification to apply to the chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartSpec *spec; + +@end + + +/** + * Updates a conditional format rule at the given index, + * or moves a conditional format rule to another index. + */ +@interface GTLRSheets_UpdateConditionalFormatRuleRequest : GTLRObject + +/** + * The zero-based index of the rule that should be replaced or moved. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *index; + +/** + * The zero-based new index the rule should end up at. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *newIndex NS_RETURNS_NOT_RETAINED; + +/** The rule that should replace the rule at the given index. */ +@property(nonatomic, strong, nullable) GTLRSheets_ConditionalFormatRule *rule; + +/** + * The sheet of the rule to move. Required if new_index is set, + * unused otherwise. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *sheetId; + +@end + + +/** + * The result of updating a conditional format rule. + */ +@interface GTLRSheets_UpdateConditionalFormatRuleResponse : GTLRObject + +/** + * The index of the new rule. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *newIndex NS_RETURNS_NOT_RETAINED; + +/** + * The new rule that replaced the old rule (if replacing), + * or the rule that was moved (if moved) + */ +@property(nonatomic, strong, nullable) GTLRSheets_ConditionalFormatRule *newRule NS_RETURNS_NOT_RETAINED; + +/** + * The old index of the rule. Not set if a rule was replaced + * (because it is the same as new_index). + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *oldIndex; + +/** + * The old (deleted) rule. Not set if a rule was moved + * (because it is the same as new_rule). + */ +@property(nonatomic, strong, nullable) GTLRSheets_ConditionalFormatRule *oldRule; + +@end + + +/** + * A request to update properties of developer metadata. + * Updates the properties of the developer metadata selected by the filters to + * the values provided in the DeveloperMetadata resource. Callers must + * specify the properties they wish to update in the fields parameter, as well + * as specify at least one DataFilter matching the metadata they wish to + * update. + */ +@interface GTLRSheets_UpdateDeveloperMetadataRequest : GTLRObject + +/** The filters matching the developer metadata entries to update. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DataFilter *> *dataFilters; + +/** + * The value that all metadata matched by the data filters will be updated to. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DeveloperMetadata *developerMetadata; + +/** + * The fields that should be updated. At least one field must be specified. + * The root `developerMetadata` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +@end + + +/** + * The response from updating developer metadata. + */ +@interface GTLRSheets_UpdateDeveloperMetadataResponse : GTLRObject + +/** The updated developer metadata. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_DeveloperMetadata *> *developerMetadata; + +@end + + +/** + * Updates the state of the specified group. + */ +@interface GTLRSheets_UpdateDimensionGroupRequest : GTLRObject + +/** + * The group whose state should be updated. The range and depth of the group + * should specify a valid group on the sheet, and all other fields updated. + */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionGroup *dimensionGroup; + +/** + * The fields that should be updated. At least one field must be specified. + * The root `dimensionGroup` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +@end + + +/** + * Updates properties of dimensions within the specified range. + */ +@interface GTLRSheets_UpdateDimensionPropertiesRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root `properties` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** Properties to update. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionProperties *properties; + +/** The rows or columns to update. */ +@property(nonatomic, strong, nullable) GTLRSheets_DimensionRange *range; + +@end + + +/** + * Update an embedded object's position (such as a moving or resizing a + * chart or image). + */ +@interface GTLRSheets_UpdateEmbeddedObjectPositionRequest : GTLRObject + +/** + * The fields of OverlayPosition + * that should be updated when setting a new position. Used only if + * newPosition.overlayPosition + * is set, in which case at least one field must + * be specified. The root `newPosition.overlayPosition` is implied and + * should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** + * An explicit position to move the embedded object to. + * If newPosition.sheetId is set, + * a new sheet with that ID will be created. + * If newPosition.newSheet is set to true, + * a new sheet will be created with an ID that will be chosen for you. + */ +@property(nonatomic, strong, nullable) GTLRSheets_EmbeddedObjectPosition *newPosition NS_RETURNS_NOT_RETAINED; + +/** + * The ID of the object to moved. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *objectId; + +@end + + +/** + * The result of updating an embedded object's position. + */ +@interface GTLRSheets_UpdateEmbeddedObjectPositionResponse : GTLRObject + +/** The new position of the embedded object. */ +@property(nonatomic, strong, nullable) GTLRSheets_EmbeddedObjectPosition *position; + +@end + + +/** + * Updates properties of the filter view. + */ +@interface GTLRSheets_UpdateFilterViewRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root `filter` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The new properties of the filter view. */ +@property(nonatomic, strong, nullable) GTLRSheets_FilterView *filter; + +@end + + +/** + * Updates properties of the named range with the specified + * namedRangeId. + */ +@interface GTLRSheets_UpdateNamedRangeRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root `namedRange` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The named range to update with the new properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_NamedRange *namedRange; + +@end + + +/** + * Updates an existing protected range with the specified + * protectedRangeId. + */ +@interface GTLRSheets_UpdateProtectedRangeRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root `protectedRange` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The protected range to update with the new properties. */ +@property(nonatomic, strong, nullable) GTLRSheets_ProtectedRange *protectedRange; + +@end + + +/** + * Updates properties of the sheet with the specified + * sheetId. + */ +@interface GTLRSheets_UpdateSheetPropertiesRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root `properties` is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The properties to update. */ +@property(nonatomic, strong, nullable) GTLRSheets_SheetProperties *properties; + +@end + + +/** + * Updates properties of a spreadsheet. + */ +@interface GTLRSheets_UpdateSpreadsheetPropertiesRequest : GTLRObject + +/** + * The fields that should be updated. At least one field must be specified. + * The root 'properties' is implied and should not be specified. + * A single `"*"` can be used as short-hand for listing every field. + * + * String format is a comma-separated list of fields. + */ +@property(nonatomic, copy, nullable) NSString *fields; + +/** The properties to update. */ +@property(nonatomic, strong, nullable) GTLRSheets_SpreadsheetProperties *properties; + +@end + + +/** + * The response when updating a range of values by a data filter in a + * spreadsheet. + */ +@interface GTLRSheets_UpdateValuesByDataFilterResponse : GTLRObject + +/** The data filter that selected the range that was updated. */ +@property(nonatomic, strong, nullable) GTLRSheets_DataFilter *dataFilter; + +/** + * The number of cells updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedCells; + +/** + * The number of columns where at least one cell in the column was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedColumns; + +/** + * The values of the cells in the range matched by the dataFilter after all + * updates were applied. This is only included if the request's + * `includeValuesInResponse` field was `true`. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ValueRange *updatedData; + +/** The range (in A1 notation) that updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *updatedRange; + +/** + * The number of rows where at least one cell in the row was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedRows; + +@end + + +/** + * The response when updating a range of values in a spreadsheet. + */ +@interface GTLRSheets_UpdateValuesResponse : GTLRObject + +/** The spreadsheet the updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * The number of cells updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedCells; + +/** + * The number of columns where at least one cell in the column was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedColumns; + +/** + * The values of the cells after updates were applied. + * This is only included if the request's `includeValuesInResponse` field + * was `true`. + */ +@property(nonatomic, strong, nullable) GTLRSheets_ValueRange *updatedData; + +/** The range (in A1 notation) that updates were applied to. */ +@property(nonatomic, copy, nullable) NSString *updatedRange; + +/** + * The number of rows where at least one cell in the row was updated. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *updatedRows; + +@end + + +/** + * Data within a range of the spreadsheet. + */ +@interface GTLRSheets_ValueRange : GTLRObject + +/** + * The major dimension of the values. + * For output, if the spreadsheet data is: `A1=1,B1=2,A2=3,B2=4`, + * then requesting `range=A1:B2,majorDimension=ROWS` will return + * `[[1,2],[3,4]]`, + * whereas requesting `range=A1:B2,majorDimension=COLUMNS` will return + * `[[1,3],[2,4]]`. + * For input, with `range=A1:B2,majorDimension=ROWS` then `[[1,2],[3,4]]` + * will set `A1=1,B1=2,A2=3,B2=4`. With `range=A1:B2,majorDimension=COLUMNS` + * then `[[1,2],[3,4]]` will set `A1=1,B1=3,A2=2,B2=4`. + * When writing, if this field is not set, it defaults to ROWS. + * + * Likely values: + * @arg @c kGTLRSheets_ValueRange_MajorDimension_Columns Operates on the + * columns of a sheet. (Value: "COLUMNS") + * @arg @c kGTLRSheets_ValueRange_MajorDimension_DimensionUnspecified The + * default value, do not use. (Value: "DIMENSION_UNSPECIFIED") + * @arg @c kGTLRSheets_ValueRange_MajorDimension_Rows Operates on the rows of + * a sheet. (Value: "ROWS") + */ +@property(nonatomic, copy, nullable) NSString *majorDimension; + +/** + * The range the values cover, in A1 notation. + * For output, this range indicates the entire requested range, + * even though the values will exclude trailing rows and columns. + * When appending values, this field represents the range to search for a + * table, after which values will be appended. + */ +@property(nonatomic, copy, nullable) NSString *range; + +/** + * The data that was read or to be written. This is an array of arrays, + * the outer array representing all the data and each inner array + * representing a major dimension. Each item in the inner array + * corresponds with one cell. + * For output, empty trailing rows and columns will not be included. + * For input, supported value types are: bool, string, and double. + * Null values will be skipped. + * To set a cell to an empty value, set the string value to an empty string. + * + * Can be any valid JSON type. + */ +@property(nonatomic, strong, nullable) NSArray<NSArray *> *values; + +@end + + +/** + * Styles for a waterfall chart column. + */ +@interface GTLRSheets_WaterfallChartColumnStyle : GTLRObject + +/** The color of the column. */ +@property(nonatomic, strong, nullable) GTLRSheets_Color *color; + +/** The label of the column's legend. */ +@property(nonatomic, copy, nullable) NSString *label; + +@end + + +/** + * A custom subtotal column for a waterfall chart series. + */ +@interface GTLRSheets_WaterfallChartCustomSubtotal : GTLRObject + +/** + * True if the data point at subtotal_index is the subtotal. If false, + * the subtotal will be computed and appear after the data point. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *dataIsSubtotal; + +/** A label for the subtotal column. */ +@property(nonatomic, copy, nullable) NSString *label; + +/** + * The 0-based index of a data point within the series. If + * data_is_subtotal is true, the data point at this index is the + * subtotal. Otherwise, the subtotal appears after the data point with + * this index. A series can have multiple subtotals at arbitrary indices, + * but subtotals do not affect the indices of the data points. For + * example, if a series has three data points, their indices will always + * be 0, 1, and 2, regardless of how many subtotals exist on the series or + * what data points they are associated with. + * + * Uses NSNumber of intValue. + */ +@property(nonatomic, strong, nullable) NSNumber *subtotalIndex; + +@end + + +/** + * The domain of a waterfall chart. + */ +@interface GTLRSheets_WaterfallChartDomain : GTLRObject + +/** The data of the WaterfallChartDomain. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *data; + +/** + * True to reverse the order of the domain values (horizontal axis). + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *reversed; + +@end + + +/** + * A single series of data for a waterfall chart. + */ +@interface GTLRSheets_WaterfallChartSeries : GTLRObject + +/** + * Custom subtotal columns appearing in this series. The order in which + * subtotals are defined is not significant. Only one subtotal may be + * defined for each data point. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_WaterfallChartCustomSubtotal *> *customSubtotals; + +/** The data being visualized in this series. */ +@property(nonatomic, strong, nullable) GTLRSheets_ChartData *data; + +/** + * True to hide the subtotal column from the end of the series. By default, + * a subtotal column will appear at the end of each series. Setting this + * field to true will hide that subtotal column for this series. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hideTrailingSubtotal; + +/** Styles for all columns in this series with negative values. */ +@property(nonatomic, strong, nullable) GTLRSheets_WaterfallChartColumnStyle *negativeColumnsStyle; + +/** Styles for all columns in this series with positive values. */ +@property(nonatomic, strong, nullable) GTLRSheets_WaterfallChartColumnStyle *positiveColumnsStyle; + +/** Styles for all subtotal columns in this series. */ +@property(nonatomic, strong, nullable) GTLRSheets_WaterfallChartColumnStyle *subtotalColumnsStyle; + +@end + + +/** + * A waterfall chart. + */ +@interface GTLRSheets_WaterfallChartSpec : GTLRObject + +/** The line style for the connector lines. */ +@property(nonatomic, strong, nullable) GTLRSheets_LineStyle *connectorLineStyle; + +/** The domain data (horizontal axis) for the waterfall chart. */ +@property(nonatomic, strong, nullable) GTLRSheets_WaterfallChartDomain *domain; + +/** + * True to interpret the first value as a total. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *firstValueIsTotal; + +/** + * True to hide connector lines between columns. + * + * Uses NSNumber of boolValue. + */ +@property(nonatomic, strong, nullable) NSNumber *hideConnectorLines; + +/** The data this waterfall chart is visualizing. */ +@property(nonatomic, strong, nullable) NSArray<GTLRSheets_WaterfallChartSeries *> *series; + +/** + * The stacked type. + * + * Likely values: + * @arg @c kGTLRSheets_WaterfallChartSpec_StackedType_Sequential Series will + * spread out along the horizontal axis. (Value: "SEQUENTIAL") + * @arg @c kGTLRSheets_WaterfallChartSpec_StackedType_Stacked Values + * corresponding to the same domain (horizontal axis) value will be + * stacked vertically. (Value: "STACKED") + * @arg @c kGTLRSheets_WaterfallChartSpec_StackedType_WaterfallStackedTypeUnspecified + * Default value, do not use. (Value: + * "WATERFALL_STACKED_TYPE_UNSPECIFIED") + */ +@property(nonatomic, copy, nullable) NSString *stackedType; + +@end + +NS_ASSUME_NONNULL_END + +#pragma clang diagnostic pop diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.m b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsObjects.m @@ -0,0 +1,2856 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#import "GTLRSheetsObjects.h" + +// ---------------------------------------------------------------------------- +// Constants + +// GTLRSheets_AppendDimensionRequest.dimension +NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_AppendDimensionRequest_Dimension_Rows = @"ROWS"; + +// GTLRSheets_BasicChartAxis.position +NSString * const kGTLRSheets_BasicChartAxis_Position_BasicChartAxisPositionUnspecified = @"BASIC_CHART_AXIS_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartAxis_Position_BottomAxis = @"BOTTOM_AXIS"; +NSString * const kGTLRSheets_BasicChartAxis_Position_LeftAxis = @"LEFT_AXIS"; +NSString * const kGTLRSheets_BasicChartAxis_Position_RightAxis = @"RIGHT_AXIS"; + +// GTLRSheets_BasicChartSeries.targetAxis +NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_BasicChartAxisPositionUnspecified = @"BASIC_CHART_AXIS_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_BottomAxis = @"BOTTOM_AXIS"; +NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_LeftAxis = @"LEFT_AXIS"; +NSString * const kGTLRSheets_BasicChartSeries_TargetAxis_RightAxis = @"RIGHT_AXIS"; + +// GTLRSheets_BasicChartSeries.type +NSString * const kGTLRSheets_BasicChartSeries_Type_Area = @"AREA"; +NSString * const kGTLRSheets_BasicChartSeries_Type_Bar = @"BAR"; +NSString * const kGTLRSheets_BasicChartSeries_Type_BasicChartTypeUnspecified = @"BASIC_CHART_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSeries_Type_Column = @"COLUMN"; +NSString * const kGTLRSheets_BasicChartSeries_Type_Combo = @"COMBO"; +NSString * const kGTLRSheets_BasicChartSeries_Type_Line = @"LINE"; +NSString * const kGTLRSheets_BasicChartSeries_Type_Scatter = @"SCATTER"; +NSString * const kGTLRSheets_BasicChartSeries_Type_SteppedArea = @"STEPPED_AREA"; + +// GTLRSheets_BasicChartSpec.chartType +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Area = @"AREA"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Bar = @"BAR"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_BasicChartTypeUnspecified = @"BASIC_CHART_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Column = @"COLUMN"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Combo = @"COMBO"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Line = @"LINE"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_Scatter = @"SCATTER"; +NSString * const kGTLRSheets_BasicChartSpec_ChartType_SteppedArea = @"STEPPED_AREA"; + +// GTLRSheets_BasicChartSpec.compareMode +NSString * const kGTLRSheets_BasicChartSpec_CompareMode_BasicChartCompareModeUnspecified = @"BASIC_CHART_COMPARE_MODE_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSpec_CompareMode_Category = @"CATEGORY"; +NSString * const kGTLRSheets_BasicChartSpec_CompareMode_Datum = @"DATUM"; + +// GTLRSheets_BasicChartSpec.legendPosition +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_BasicChartLegendPositionUnspecified = @"BASIC_CHART_LEGEND_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_BottomLegend = @"BOTTOM_LEGEND"; +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_LeftLegend = @"LEFT_LEGEND"; +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_NoLegend = @"NO_LEGEND"; +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_RightLegend = @"RIGHT_LEGEND"; +NSString * const kGTLRSheets_BasicChartSpec_LegendPosition_TopLegend = @"TOP_LEGEND"; + +// GTLRSheets_BasicChartSpec.stackedType +NSString * const kGTLRSheets_BasicChartSpec_StackedType_BasicChartStackedTypeUnspecified = @"BASIC_CHART_STACKED_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_BasicChartSpec_StackedType_NotStacked = @"NOT_STACKED"; +NSString * const kGTLRSheets_BasicChartSpec_StackedType_PercentStacked = @"PERCENT_STACKED"; +NSString * const kGTLRSheets_BasicChartSpec_StackedType_Stacked = @"STACKED"; + +// GTLRSheets_BatchGetValuesByDataFilterRequest.dateTimeRenderOption +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_FormattedString = @"FORMATTED_STRING"; +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_DateTimeRenderOption_SerialNumber = @"SERIAL_NUMBER"; + +// GTLRSheets_BatchGetValuesByDataFilterRequest.majorDimension +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_MajorDimension_Rows = @"ROWS"; + +// GTLRSheets_BatchGetValuesByDataFilterRequest.valueRenderOption +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_FormattedValue = @"FORMATTED_VALUE"; +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_Formula = @"FORMULA"; +NSString * const kGTLRSheets_BatchGetValuesByDataFilterRequest_ValueRenderOption_UnformattedValue = @"UNFORMATTED_VALUE"; + +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.responseDateTimeRenderOption +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_FormattedString = @"FORMATTED_STRING"; +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseDateTimeRenderOption_SerialNumber = @"SERIAL_NUMBER"; + +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.responseValueRenderOption +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_FormattedValue = @"FORMATTED_VALUE"; +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_Formula = @"FORMULA"; +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ResponseValueRenderOption_UnformattedValue = @"UNFORMATTED_VALUE"; + +// GTLRSheets_BatchUpdateValuesByDataFilterRequest.valueInputOption +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_InputValueOptionUnspecified = @"INPUT_VALUE_OPTION_UNSPECIFIED"; +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_Raw = @"RAW"; +NSString * const kGTLRSheets_BatchUpdateValuesByDataFilterRequest_ValueInputOption_UserEntered = @"USER_ENTERED"; + +// GTLRSheets_BatchUpdateValuesRequest.responseDateTimeRenderOption +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_FormattedString = @"FORMATTED_STRING"; +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseDateTimeRenderOption_SerialNumber = @"SERIAL_NUMBER"; + +// GTLRSheets_BatchUpdateValuesRequest.responseValueRenderOption +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_FormattedValue = @"FORMATTED_VALUE"; +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_Formula = @"FORMULA"; +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ResponseValueRenderOption_UnformattedValue = @"UNFORMATTED_VALUE"; + +// GTLRSheets_BatchUpdateValuesRequest.valueInputOption +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_InputValueOptionUnspecified = @"INPUT_VALUE_OPTION_UNSPECIFIED"; +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_Raw = @"RAW"; +NSString * const kGTLRSheets_BatchUpdateValuesRequest_ValueInputOption_UserEntered = @"USER_ENTERED"; + +// GTLRSheets_BooleanCondition.type +NSString * const kGTLRSheets_BooleanCondition_Type_Blank = @"BLANK"; +NSString * const kGTLRSheets_BooleanCondition_Type_Boolean = @"BOOLEAN"; +NSString * const kGTLRSheets_BooleanCondition_Type_ConditionTypeUnspecified = @"CONDITION_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_BooleanCondition_Type_CustomFormula = @"CUSTOM_FORMULA"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateAfter = @"DATE_AFTER"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateBefore = @"DATE_BEFORE"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateBetween = @"DATE_BETWEEN"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateEq = @"DATE_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateIsValid = @"DATE_IS_VALID"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateNotBetween = @"DATE_NOT_BETWEEN"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateOnOrAfter = @"DATE_ON_OR_AFTER"; +NSString * const kGTLRSheets_BooleanCondition_Type_DateOnOrBefore = @"DATE_ON_OR_BEFORE"; +NSString * const kGTLRSheets_BooleanCondition_Type_NotBlank = @"NOT_BLANK"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberBetween = @"NUMBER_BETWEEN"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberEq = @"NUMBER_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberGreater = @"NUMBER_GREATER"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberGreaterThanEq = @"NUMBER_GREATER_THAN_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberLess = @"NUMBER_LESS"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberLessThanEq = @"NUMBER_LESS_THAN_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberNotBetween = @"NUMBER_NOT_BETWEEN"; +NSString * const kGTLRSheets_BooleanCondition_Type_NumberNotEq = @"NUMBER_NOT_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_OneOfList = @"ONE_OF_LIST"; +NSString * const kGTLRSheets_BooleanCondition_Type_OneOfRange = @"ONE_OF_RANGE"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextContains = @"TEXT_CONTAINS"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextEndsWith = @"TEXT_ENDS_WITH"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextEq = @"TEXT_EQ"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextIsEmail = @"TEXT_IS_EMAIL"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextIsUrl = @"TEXT_IS_URL"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextNotContains = @"TEXT_NOT_CONTAINS"; +NSString * const kGTLRSheets_BooleanCondition_Type_TextStartsWith = @"TEXT_STARTS_WITH"; + +// GTLRSheets_Border.style +NSString * const kGTLRSheets_Border_Style_Dashed = @"DASHED"; +NSString * const kGTLRSheets_Border_Style_Dotted = @"DOTTED"; +NSString * const kGTLRSheets_Border_Style_Double = @"DOUBLE"; +NSString * const kGTLRSheets_Border_Style_None = @"NONE"; +NSString * const kGTLRSheets_Border_Style_Solid = @"SOLID"; +NSString * const kGTLRSheets_Border_Style_SolidMedium = @"SOLID_MEDIUM"; +NSString * const kGTLRSheets_Border_Style_SolidThick = @"SOLID_THICK"; +NSString * const kGTLRSheets_Border_Style_StyleUnspecified = @"STYLE_UNSPECIFIED"; + +// GTLRSheets_BubbleChartSpec.legendPosition +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_BottomLegend = @"BOTTOM_LEGEND"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_BubbleChartLegendPositionUnspecified = @"BUBBLE_CHART_LEGEND_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_InsideLegend = @"INSIDE_LEGEND"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_LeftLegend = @"LEFT_LEGEND"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_NoLegend = @"NO_LEGEND"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_RightLegend = @"RIGHT_LEGEND"; +NSString * const kGTLRSheets_BubbleChartSpec_LegendPosition_TopLegend = @"TOP_LEGEND"; + +// GTLRSheets_CellFormat.horizontalAlignment +NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Center = @"CENTER"; +NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_HorizontalAlignUnspecified = @"HORIZONTAL_ALIGN_UNSPECIFIED"; +NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Left = @"LEFT"; +NSString * const kGTLRSheets_CellFormat_HorizontalAlignment_Right = @"RIGHT"; + +// GTLRSheets_CellFormat.hyperlinkDisplayType +NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_HyperlinkDisplayTypeUnspecified = @"HYPERLINK_DISPLAY_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_Linked = @"LINKED"; +NSString * const kGTLRSheets_CellFormat_HyperlinkDisplayType_PlainText = @"PLAIN_TEXT"; + +// GTLRSheets_CellFormat.textDirection +NSString * const kGTLRSheets_CellFormat_TextDirection_LeftToRight = @"LEFT_TO_RIGHT"; +NSString * const kGTLRSheets_CellFormat_TextDirection_RightToLeft = @"RIGHT_TO_LEFT"; +NSString * const kGTLRSheets_CellFormat_TextDirection_TextDirectionUnspecified = @"TEXT_DIRECTION_UNSPECIFIED"; + +// GTLRSheets_CellFormat.verticalAlignment +NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Bottom = @"BOTTOM"; +NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Middle = @"MIDDLE"; +NSString * const kGTLRSheets_CellFormat_VerticalAlignment_Top = @"TOP"; +NSString * const kGTLRSheets_CellFormat_VerticalAlignment_VerticalAlignUnspecified = @"VERTICAL_ALIGN_UNSPECIFIED"; + +// GTLRSheets_CellFormat.wrapStrategy +NSString * const kGTLRSheets_CellFormat_WrapStrategy_Clip = @"CLIP"; +NSString * const kGTLRSheets_CellFormat_WrapStrategy_LegacyWrap = @"LEGACY_WRAP"; +NSString * const kGTLRSheets_CellFormat_WrapStrategy_OverflowCell = @"OVERFLOW_CELL"; +NSString * const kGTLRSheets_CellFormat_WrapStrategy_Wrap = @"WRAP"; +NSString * const kGTLRSheets_CellFormat_WrapStrategy_WrapStrategyUnspecified = @"WRAP_STRATEGY_UNSPECIFIED"; + +// GTLRSheets_ChartSpec.hiddenDimensionStrategy +NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ChartHiddenDimensionStrategyUnspecified = @"CHART_HIDDEN_DIMENSION_STRATEGY_UNSPECIFIED"; +NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_ShowAll = @"SHOW_ALL"; +NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenColumns = @"SKIP_HIDDEN_COLUMNS"; +NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRows = @"SKIP_HIDDEN_ROWS"; +NSString * const kGTLRSheets_ChartSpec_HiddenDimensionStrategy_SkipHiddenRowsAndColumns = @"SKIP_HIDDEN_ROWS_AND_COLUMNS"; + +// GTLRSheets_ConditionValue.relativeDate +NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastMonth = @"PAST_MONTH"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastWeek = @"PAST_WEEK"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_PastYear = @"PAST_YEAR"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_RelativeDateUnspecified = @"RELATIVE_DATE_UNSPECIFIED"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_Today = @"TODAY"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_Tomorrow = @"TOMORROW"; +NSString * const kGTLRSheets_ConditionValue_RelativeDate_Yesterday = @"YESTERDAY"; + +// GTLRSheets_CopyPasteRequest.pasteOrientation +NSString * const kGTLRSheets_CopyPasteRequest_PasteOrientation_Normal = @"NORMAL"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteOrientation_Transpose = @"TRANSPOSE"; + +// GTLRSheets_CopyPasteRequest.pasteType +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteConditionalFormatting = @"PASTE_CONDITIONAL_FORMATTING"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteDataValidation = @"PASTE_DATA_VALIDATION"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteFormat = @"PASTE_FORMAT"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteFormula = @"PASTE_FORMULA"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteNoBorders = @"PASTE_NO_BORDERS"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteNormal = @"PASTE_NORMAL"; +NSString * const kGTLRSheets_CopyPasteRequest_PasteType_PasteValues = @"PASTE_VALUES"; + +// GTLRSheets_CutPasteRequest.pasteType +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteConditionalFormatting = @"PASTE_CONDITIONAL_FORMATTING"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteDataValidation = @"PASTE_DATA_VALIDATION"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteFormat = @"PASTE_FORMAT"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteFormula = @"PASTE_FORMULA"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteNoBorders = @"PASTE_NO_BORDERS"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteNormal = @"PASTE_NORMAL"; +NSString * const kGTLRSheets_CutPasteRequest_PasteType_PasteValues = @"PASTE_VALUES"; + +// GTLRSheets_DataFilterValueRange.majorDimension +NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_DataFilterValueRange_MajorDimension_Rows = @"ROWS"; + +// GTLRSheets_DateTimeRule.type +NSString * const kGTLRSheets_DateTimeRule_Type_DateTimeRuleTypeUnspecified = @"DATE_TIME_RULE_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_DateTimeRule_Type_DayMonth = @"DAY_MONTH"; +NSString * const kGTLRSheets_DateTimeRule_Type_DayOfMonth = @"DAY_OF_MONTH"; +NSString * const kGTLRSheets_DateTimeRule_Type_DayOfWeek = @"DAY_OF_WEEK"; +NSString * const kGTLRSheets_DateTimeRule_Type_DayOfYear = @"DAY_OF_YEAR"; +NSString * const kGTLRSheets_DateTimeRule_Type_Hour = @"HOUR"; +NSString * const kGTLRSheets_DateTimeRule_Type_HourMinute = @"HOUR_MINUTE"; +NSString * const kGTLRSheets_DateTimeRule_Type_HourMinuteAmpm = @"HOUR_MINUTE_AMPM"; +NSString * const kGTLRSheets_DateTimeRule_Type_Minute = @"MINUTE"; +NSString * const kGTLRSheets_DateTimeRule_Type_Month = @"MONTH"; +NSString * const kGTLRSheets_DateTimeRule_Type_Quarter = @"QUARTER"; +NSString * const kGTLRSheets_DateTimeRule_Type_Second = @"SECOND"; +NSString * const kGTLRSheets_DateTimeRule_Type_Year = @"YEAR"; +NSString * const kGTLRSheets_DateTimeRule_Type_YearMonth = @"YEAR_MONTH"; +NSString * const kGTLRSheets_DateTimeRule_Type_YearMonthDay = @"YEAR_MONTH_DAY"; +NSString * const kGTLRSheets_DateTimeRule_Type_YearQuarter = @"YEAR_QUARTER"; + +// GTLRSheets_DeleteRangeRequest.shiftDimension +NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_DeleteRangeRequest_ShiftDimension_Rows = @"ROWS"; + +// GTLRSheets_DeveloperMetadata.visibility +NSString * const kGTLRSheets_DeveloperMetadata_Visibility_DeveloperMetadataVisibilityUnspecified = @"DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED"; +NSString * const kGTLRSheets_DeveloperMetadata_Visibility_Document = @"DOCUMENT"; +NSString * const kGTLRSheets_DeveloperMetadata_Visibility_Project = @"PROJECT"; + +// GTLRSheets_DeveloperMetadataLocation.locationType +NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Column = @"COLUMN"; +NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_DeveloperMetadataLocationTypeUnspecified = @"DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Row = @"ROW"; +NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Sheet = @"SHEET"; +NSString * const kGTLRSheets_DeveloperMetadataLocation_LocationType_Spreadsheet = @"SPREADSHEET"; + +// GTLRSheets_DeveloperMetadataLookup.locationMatchingStrategy +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_DeveloperMetadataLocationMatchingStrategyUnspecified = @"DEVELOPER_METADATA_LOCATION_MATCHING_STRATEGY_UNSPECIFIED"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_ExactLocation = @"EXACT_LOCATION"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationMatchingStrategy_IntersectingLocation = @"INTERSECTING_LOCATION"; + +// GTLRSheets_DeveloperMetadataLookup.locationType +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Column = @"COLUMN"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_DeveloperMetadataLocationTypeUnspecified = @"DEVELOPER_METADATA_LOCATION_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Row = @"ROW"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Sheet = @"SHEET"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_LocationType_Spreadsheet = @"SPREADSHEET"; + +// GTLRSheets_DeveloperMetadataLookup.visibility +NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_DeveloperMetadataVisibilityUnspecified = @"DEVELOPER_METADATA_VISIBILITY_UNSPECIFIED"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_Document = @"DOCUMENT"; +NSString * const kGTLRSheets_DeveloperMetadataLookup_Visibility_Project = @"PROJECT"; + +// GTLRSheets_DimensionRange.dimension +NSString * const kGTLRSheets_DimensionRange_Dimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_DimensionRange_Dimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_DimensionRange_Dimension_Rows = @"ROWS"; + +// GTLRSheets_ErrorValue.type +NSString * const kGTLRSheets_ErrorValue_Type_DivideByZero = @"DIVIDE_BY_ZERO"; +NSString * const kGTLRSheets_ErrorValue_Type_Error = @"ERROR"; +NSString * const kGTLRSheets_ErrorValue_Type_ErrorTypeUnspecified = @"ERROR_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_ErrorValue_Type_Loading = @"LOADING"; +NSString * const kGTLRSheets_ErrorValue_Type_NA = @"N_A"; +NSString * const kGTLRSheets_ErrorValue_Type_Name = @"NAME"; +NSString * const kGTLRSheets_ErrorValue_Type_NullValue = @"NULL_VALUE"; +NSString * const kGTLRSheets_ErrorValue_Type_Num = @"NUM"; +NSString * const kGTLRSheets_ErrorValue_Type_Ref = @"REF"; +NSString * const kGTLRSheets_ErrorValue_Type_Value = @"VALUE"; + +// GTLRSheets_HistogramChartSpec.legendPosition +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_BottomLegend = @"BOTTOM_LEGEND"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_HistogramChartLegendPositionUnspecified = @"HISTOGRAM_CHART_LEGEND_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_InsideLegend = @"INSIDE_LEGEND"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_LeftLegend = @"LEFT_LEGEND"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_NoLegend = @"NO_LEGEND"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_RightLegend = @"RIGHT_LEGEND"; +NSString * const kGTLRSheets_HistogramChartSpec_LegendPosition_TopLegend = @"TOP_LEGEND"; + +// GTLRSheets_InsertRangeRequest.shiftDimension +NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_InsertRangeRequest_ShiftDimension_Rows = @"ROWS"; + +// GTLRSheets_InterpolationPoint.type +NSString * const kGTLRSheets_InterpolationPoint_Type_InterpolationPointTypeUnspecified = @"INTERPOLATION_POINT_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_InterpolationPoint_Type_Max = @"MAX"; +NSString * const kGTLRSheets_InterpolationPoint_Type_Min = @"MIN"; +NSString * const kGTLRSheets_InterpolationPoint_Type_Number = @"NUMBER"; +NSString * const kGTLRSheets_InterpolationPoint_Type_Percent = @"PERCENT"; +NSString * const kGTLRSheets_InterpolationPoint_Type_Percentile = @"PERCENTILE"; + +// GTLRSheets_LineStyle.type +NSString * const kGTLRSheets_LineStyle_Type_Custom = @"CUSTOM"; +NSString * const kGTLRSheets_LineStyle_Type_Dotted = @"DOTTED"; +NSString * const kGTLRSheets_LineStyle_Type_Invisible = @"INVISIBLE"; +NSString * const kGTLRSheets_LineStyle_Type_LineDashTypeUnspecified = @"LINE_DASH_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_LineStyle_Type_LongDashed = @"LONG_DASHED"; +NSString * const kGTLRSheets_LineStyle_Type_LongDashedDotted = @"LONG_DASHED_DOTTED"; +NSString * const kGTLRSheets_LineStyle_Type_MediumDashed = @"MEDIUM_DASHED"; +NSString * const kGTLRSheets_LineStyle_Type_MediumDashedDotted = @"MEDIUM_DASHED_DOTTED"; +NSString * const kGTLRSheets_LineStyle_Type_Solid = @"SOLID"; + +// GTLRSheets_MergeCellsRequest.mergeType +NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeAll = @"MERGE_ALL"; +NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeColumns = @"MERGE_COLUMNS"; +NSString * const kGTLRSheets_MergeCellsRequest_MergeType_MergeRows = @"MERGE_ROWS"; + +// GTLRSheets_NumberFormat.type +NSString * const kGTLRSheets_NumberFormat_Type_Currency = @"CURRENCY"; +NSString * const kGTLRSheets_NumberFormat_Type_Date = @"DATE"; +NSString * const kGTLRSheets_NumberFormat_Type_DateTime = @"DATE_TIME"; +NSString * const kGTLRSheets_NumberFormat_Type_Number = @"NUMBER"; +NSString * const kGTLRSheets_NumberFormat_Type_NumberFormatTypeUnspecified = @"NUMBER_FORMAT_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_NumberFormat_Type_Percent = @"PERCENT"; +NSString * const kGTLRSheets_NumberFormat_Type_Scientific = @"SCIENTIFIC"; +NSString * const kGTLRSheets_NumberFormat_Type_Text = @"TEXT"; +NSString * const kGTLRSheets_NumberFormat_Type_Time = @"TIME"; + +// GTLRSheets_OrgChartSpec.nodeSize +NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Large = @"LARGE"; +NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Medium = @"MEDIUM"; +NSString * const kGTLRSheets_OrgChartSpec_NodeSize_OrgChartLabelSizeUnspecified = @"ORG_CHART_LABEL_SIZE_UNSPECIFIED"; +NSString * const kGTLRSheets_OrgChartSpec_NodeSize_Small = @"SMALL"; + +// GTLRSheets_PasteDataRequest.type +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteConditionalFormatting = @"PASTE_CONDITIONAL_FORMATTING"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteDataValidation = @"PASTE_DATA_VALIDATION"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteFormat = @"PASTE_FORMAT"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteFormula = @"PASTE_FORMULA"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteNoBorders = @"PASTE_NO_BORDERS"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteNormal = @"PASTE_NORMAL"; +NSString * const kGTLRSheets_PasteDataRequest_Type_PasteValues = @"PASTE_VALUES"; + +// GTLRSheets_PieChartSpec.legendPosition +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_BottomLegend = @"BOTTOM_LEGEND"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_LabeledLegend = @"LABELED_LEGEND"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_LeftLegend = @"LEFT_LEGEND"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_NoLegend = @"NO_LEGEND"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_PieChartLegendPositionUnspecified = @"PIE_CHART_LEGEND_POSITION_UNSPECIFIED"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_RightLegend = @"RIGHT_LEGEND"; +NSString * const kGTLRSheets_PieChartSpec_LegendPosition_TopLegend = @"TOP_LEGEND"; + +// GTLRSheets_PivotGroup.sortOrder +NSString * const kGTLRSheets_PivotGroup_SortOrder_Ascending = @"ASCENDING"; +NSString * const kGTLRSheets_PivotGroup_SortOrder_Descending = @"DESCENDING"; +NSString * const kGTLRSheets_PivotGroup_SortOrder_SortOrderUnspecified = @"SORT_ORDER_UNSPECIFIED"; + +// GTLRSheets_PivotTable.valueLayout +NSString * const kGTLRSheets_PivotTable_ValueLayout_Horizontal = @"HORIZONTAL"; +NSString * const kGTLRSheets_PivotTable_ValueLayout_Vertical = @"VERTICAL"; + +// GTLRSheets_PivotValue.calculatedDisplayType +NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfColumnTotal = @"PERCENT_OF_COLUMN_TOTAL"; +NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfGrandTotal = @"PERCENT_OF_GRAND_TOTAL"; +NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PercentOfRowTotal = @"PERCENT_OF_ROW_TOTAL"; +NSString * const kGTLRSheets_PivotValue_CalculatedDisplayType_PivotValueCalculatedDisplayTypeUnspecified = @"PIVOT_VALUE_CALCULATED_DISPLAY_TYPE_UNSPECIFIED"; + +// GTLRSheets_PivotValue.summarizeFunction +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Average = @"AVERAGE"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Count = @"COUNT"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Counta = @"COUNTA"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Countunique = @"COUNTUNIQUE"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Custom = @"CUSTOM"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Max = @"MAX"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Median = @"MEDIAN"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Min = @"MIN"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_PivotStandardValueFunctionUnspecified = @"PIVOT_STANDARD_VALUE_FUNCTION_UNSPECIFIED"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Product = @"PRODUCT"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Stdev = @"STDEV"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Stdevp = @"STDEVP"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Sum = @"SUM"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Var = @"VAR"; +NSString * const kGTLRSheets_PivotValue_SummarizeFunction_Varp = @"VARP"; + +// GTLRSheets_SheetProperties.sheetType +NSString * const kGTLRSheets_SheetProperties_SheetType_Grid = @"GRID"; +NSString * const kGTLRSheets_SheetProperties_SheetType_Object = @"OBJECT"; +NSString * const kGTLRSheets_SheetProperties_SheetType_SheetTypeUnspecified = @"SHEET_TYPE_UNSPECIFIED"; + +// GTLRSheets_SortSpec.sortOrder +NSString * const kGTLRSheets_SortSpec_SortOrder_Ascending = @"ASCENDING"; +NSString * const kGTLRSheets_SortSpec_SortOrder_Descending = @"DESCENDING"; +NSString * const kGTLRSheets_SortSpec_SortOrder_SortOrderUnspecified = @"SORT_ORDER_UNSPECIFIED"; + +// GTLRSheets_SourceAndDestination.dimension +NSString * const kGTLRSheets_SourceAndDestination_Dimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_SourceAndDestination_Dimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_SourceAndDestination_Dimension_Rows = @"ROWS"; + +// GTLRSheets_SpreadsheetProperties.autoRecalc +NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_Hour = @"HOUR"; +NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_Minute = @"MINUTE"; +NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_OnChange = @"ON_CHANGE"; +NSString * const kGTLRSheets_SpreadsheetProperties_AutoRecalc_RecalculationIntervalUnspecified = @"RECALCULATION_INTERVAL_UNSPECIFIED"; + +// GTLRSheets_TextPosition.horizontalAlignment +NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Center = @"CENTER"; +NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_HorizontalAlignUnspecified = @"HORIZONTAL_ALIGN_UNSPECIFIED"; +NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Left = @"LEFT"; +NSString * const kGTLRSheets_TextPosition_HorizontalAlignment_Right = @"RIGHT"; + +// GTLRSheets_TextToColumnsRequest.delimiterType +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Autodetect = @"AUTODETECT"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Comma = @"COMMA"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Custom = @"CUSTOM"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_DelimiterTypeUnspecified = @"DELIMITER_TYPE_UNSPECIFIED"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Period = @"PERIOD"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Semicolon = @"SEMICOLON"; +NSString * const kGTLRSheets_TextToColumnsRequest_DelimiterType_Space = @"SPACE"; + +// GTLRSheets_ValueRange.majorDimension +NSString * const kGTLRSheets_ValueRange_MajorDimension_Columns = @"COLUMNS"; +NSString * const kGTLRSheets_ValueRange_MajorDimension_DimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheets_ValueRange_MajorDimension_Rows = @"ROWS"; + +// GTLRSheets_WaterfallChartSpec.stackedType +NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_Sequential = @"SEQUENTIAL"; +NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_Stacked = @"STACKED"; +NSString * const kGTLRSheets_WaterfallChartSpec_StackedType_WaterfallStackedTypeUnspecified = @"WATERFALL_STACKED_TYPE_UNSPECIFIED"; + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddBandingRequest +// + +@implementation GTLRSheets_AddBandingRequest +@dynamic bandedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddBandingResponse +// + +@implementation GTLRSheets_AddBandingResponse +@dynamic bandedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddChartRequest +// + +@implementation GTLRSheets_AddChartRequest +@dynamic chart; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddChartResponse +// + +@implementation GTLRSheets_AddChartResponse +@dynamic chart; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddConditionalFormatRuleRequest +// + +@implementation GTLRSheets_AddConditionalFormatRuleRequest +@dynamic index, rule; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddDimensionGroupRequest +// + +@implementation GTLRSheets_AddDimensionGroupRequest +@dynamic range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddDimensionGroupResponse +// + +@implementation GTLRSheets_AddDimensionGroupResponse +@dynamic dimensionGroups; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dimensionGroups" : [GTLRSheets_DimensionGroup class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddFilterViewRequest +// + +@implementation GTLRSheets_AddFilterViewRequest +@dynamic filter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddFilterViewResponse +// + +@implementation GTLRSheets_AddFilterViewResponse +@dynamic filter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddNamedRangeRequest +// + +@implementation GTLRSheets_AddNamedRangeRequest +@dynamic namedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddNamedRangeResponse +// + +@implementation GTLRSheets_AddNamedRangeResponse +@dynamic namedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddProtectedRangeRequest +// + +@implementation GTLRSheets_AddProtectedRangeRequest +@dynamic protectedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddProtectedRangeResponse +// + +@implementation GTLRSheets_AddProtectedRangeResponse +@dynamic protectedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddSheetRequest +// + +@implementation GTLRSheets_AddSheetRequest +@dynamic properties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AddSheetResponse +// + +@implementation GTLRSheets_AddSheetResponse +@dynamic properties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AppendCellsRequest +// + +@implementation GTLRSheets_AppendCellsRequest +@dynamic fields, rows, sheetId; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"rows" : [GTLRSheets_RowData class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AppendDimensionRequest +// + +@implementation GTLRSheets_AppendDimensionRequest +@dynamic dimension, length, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AppendValuesResponse +// + +@implementation GTLRSheets_AppendValuesResponse +@dynamic spreadsheetId, tableRange, updates; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AutoFillRequest +// + +@implementation GTLRSheets_AutoFillRequest +@dynamic range, sourceAndDestination, useAlternateSeries; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_AutoResizeDimensionsRequest +// + +@implementation GTLRSheets_AutoResizeDimensionsRequest +@dynamic dimensions; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BandedRange +// + +@implementation GTLRSheets_BandedRange +@dynamic bandedRangeId, columnProperties, range, rowProperties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BandingProperties +// + +@implementation GTLRSheets_BandingProperties +@dynamic firstBandColor, footerColor, headerColor, secondBandColor; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicChartAxis +// + +@implementation GTLRSheets_BasicChartAxis +@dynamic format, position, title, titleTextPosition; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicChartDomain +// + +@implementation GTLRSheets_BasicChartDomain +@dynamic domain, reversed; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicChartSeries +// + +@implementation GTLRSheets_BasicChartSeries +@dynamic color, lineStyle, series, targetAxis, type; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicChartSpec +// + +@implementation GTLRSheets_BasicChartSpec +@dynamic axis, chartType, compareMode, domains, headerCount, interpolateNulls, + legendPosition, lineSmoothing, series, stackedType, threeDimensional; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"axis" : [GTLRSheets_BasicChartAxis class], + @"domains" : [GTLRSheets_BasicChartDomain class], + @"series" : [GTLRSheets_BasicChartSeries class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicFilter +// + +@implementation GTLRSheets_BasicFilter +@dynamic criteria, range, sortSpecs; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"sortSpecs" : [GTLRSheets_SortSpec class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BasicFilter_Criteria +// + +@implementation GTLRSheets_BasicFilter_Criteria + ++ (Class)classForAdditionalProperties { + return [GTLRSheets_FilterCriteria class]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchClearValuesByDataFilterRequest +// + +@implementation GTLRSheets_BatchClearValuesByDataFilterRequest +@dynamic dataFilters; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchClearValuesByDataFilterResponse +// + +@implementation GTLRSheets_BatchClearValuesByDataFilterResponse +@dynamic clearedRanges, spreadsheetId; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"clearedRanges" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchClearValuesRequest +// + +@implementation GTLRSheets_BatchClearValuesRequest +@dynamic ranges; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"ranges" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchClearValuesResponse +// + +@implementation GTLRSheets_BatchClearValuesResponse +@dynamic clearedRanges, spreadsheetId; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"clearedRanges" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchGetValuesByDataFilterRequest +// + +@implementation GTLRSheets_BatchGetValuesByDataFilterRequest +@dynamic dataFilters, dateTimeRenderOption, majorDimension, valueRenderOption; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchGetValuesByDataFilterResponse +// + +@implementation GTLRSheets_BatchGetValuesByDataFilterResponse +@dynamic spreadsheetId, valueRanges; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"valueRanges" : [GTLRSheets_MatchedValueRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchGetValuesResponse +// + +@implementation GTLRSheets_BatchGetValuesResponse +@dynamic spreadsheetId, valueRanges; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"valueRanges" : [GTLRSheets_ValueRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateSpreadsheetRequest +// + +@implementation GTLRSheets_BatchUpdateSpreadsheetRequest +@dynamic includeSpreadsheetInResponse, requests, responseIncludeGridData, + responseRanges; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"requests" : [GTLRSheets_Request class], + @"responseRanges" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateSpreadsheetResponse +// + +@implementation GTLRSheets_BatchUpdateSpreadsheetResponse +@dynamic replies, spreadsheetId, updatedSpreadsheet; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"replies" : [GTLRSheets_Response class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateValuesByDataFilterRequest +// + +@implementation GTLRSheets_BatchUpdateValuesByDataFilterRequest +@dynamic data, includeValuesInResponse, responseDateTimeRenderOption, + responseValueRenderOption, valueInputOption; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"data" : [GTLRSheets_DataFilterValueRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateValuesByDataFilterResponse +// + +@implementation GTLRSheets_BatchUpdateValuesByDataFilterResponse +@dynamic responses, spreadsheetId, totalUpdatedCells, totalUpdatedColumns, + totalUpdatedRows, totalUpdatedSheets; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"responses" : [GTLRSheets_UpdateValuesByDataFilterResponse class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateValuesRequest +// + +@implementation GTLRSheets_BatchUpdateValuesRequest +@dynamic data, includeValuesInResponse, responseDateTimeRenderOption, + responseValueRenderOption, valueInputOption; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"data" : [GTLRSheets_ValueRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BatchUpdateValuesResponse +// + +@implementation GTLRSheets_BatchUpdateValuesResponse +@dynamic responses, spreadsheetId, totalUpdatedCells, totalUpdatedColumns, + totalUpdatedRows, totalUpdatedSheets; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"responses" : [GTLRSheets_UpdateValuesResponse class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BooleanCondition +// + +@implementation GTLRSheets_BooleanCondition +@dynamic type, values; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"values" : [GTLRSheets_ConditionValue class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BooleanRule +// + +@implementation GTLRSheets_BooleanRule +@dynamic condition, format; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Border +// + +@implementation GTLRSheets_Border +@dynamic color, style, width; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Borders +// + +@implementation GTLRSheets_Borders +@dynamic bottom, left, right, top; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_BubbleChartSpec +// + +@implementation GTLRSheets_BubbleChartSpec +@dynamic bubbleBorderColor, bubbleLabels, bubbleMaxRadiusSize, + bubbleMinRadiusSize, bubbleOpacity, bubbleSizes, bubbleTextStyle, + domain, groupIds, legendPosition, series; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CandlestickChartSpec +// + +@implementation GTLRSheets_CandlestickChartSpec +@dynamic data, domain; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"data" : [GTLRSheets_CandlestickData class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CandlestickData +// + +@implementation GTLRSheets_CandlestickData +@dynamic closeSeries, highSeries, lowSeries, openSeries; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CandlestickDomain +// + +@implementation GTLRSheets_CandlestickDomain +@dynamic data, reversed; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CandlestickSeries +// + +@implementation GTLRSheets_CandlestickSeries +@dynamic data; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CellData +// + +@implementation GTLRSheets_CellData +@dynamic dataValidation, effectiveFormat, effectiveValue, formattedValue, + hyperlink, note, pivotTable, textFormatRuns, userEnteredFormat, + userEnteredValue; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"textFormatRuns" : [GTLRSheets_TextFormatRun class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CellFormat +// + +@implementation GTLRSheets_CellFormat +@dynamic backgroundColor, borders, horizontalAlignment, hyperlinkDisplayType, + numberFormat, padding, textDirection, textFormat, textRotation, + verticalAlignment, wrapStrategy; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ChartData +// + +@implementation GTLRSheets_ChartData +@dynamic sourceRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ChartSourceRange +// + +@implementation GTLRSheets_ChartSourceRange +@dynamic sources; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"sources" : [GTLRSheets_GridRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ChartSpec +// + +@implementation GTLRSheets_ChartSpec +@dynamic altText, backgroundColor, basicChart, bubbleChart, candlestickChart, + fontName, hiddenDimensionStrategy, histogramChart, maximized, orgChart, + pieChart, subtitle, subtitleTextFormat, subtitleTextPosition, title, + titleTextFormat, titleTextPosition, treemapChart, waterfallChart; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ClearBasicFilterRequest +// + +@implementation GTLRSheets_ClearBasicFilterRequest +@dynamic sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ClearValuesRequest +// + +@implementation GTLRSheets_ClearValuesRequest +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ClearValuesResponse +// + +@implementation GTLRSheets_ClearValuesResponse +@dynamic clearedRange, spreadsheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Color +// + +@implementation GTLRSheets_Color +@dynamic alpha, blue, green, red; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ConditionalFormatRule +// + +@implementation GTLRSheets_ConditionalFormatRule +@dynamic booleanRule, gradientRule, ranges; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"ranges" : [GTLRSheets_GridRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ConditionValue +// + +@implementation GTLRSheets_ConditionValue +@dynamic relativeDate, userEnteredValue; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CopyPasteRequest +// + +@implementation GTLRSheets_CopyPasteRequest +@dynamic destination, pasteOrientation, pasteType, source; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CopySheetToAnotherSpreadsheetRequest +// + +@implementation GTLRSheets_CopySheetToAnotherSpreadsheetRequest +@dynamic destinationSpreadsheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CreateDeveloperMetadataRequest +// + +@implementation GTLRSheets_CreateDeveloperMetadataRequest +@dynamic developerMetadata; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CreateDeveloperMetadataResponse +// + +@implementation GTLRSheets_CreateDeveloperMetadataResponse +@dynamic developerMetadata; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_CutPasteRequest +// + +@implementation GTLRSheets_CutPasteRequest +@dynamic destination, pasteType, source; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DataFilter +// + +@implementation GTLRSheets_DataFilter +@dynamic a1Range, developerMetadataLookup, gridRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DataFilterValueRange +// + +@implementation GTLRSheets_DataFilterValueRange +@dynamic dataFilter, majorDimension, values; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"values" : [NSObject class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DataValidationRule +// + +@implementation GTLRSheets_DataValidationRule +@dynamic condition, inputMessage, showCustomUi, strict; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DateTimeRule +// + +@implementation GTLRSheets_DateTimeRule +@dynamic type; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteBandingRequest +// + +@implementation GTLRSheets_DeleteBandingRequest +@dynamic bandedRangeId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteConditionalFormatRuleRequest +// + +@implementation GTLRSheets_DeleteConditionalFormatRuleRequest +@dynamic index, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteConditionalFormatRuleResponse +// + +@implementation GTLRSheets_DeleteConditionalFormatRuleResponse +@dynamic rule; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteDeveloperMetadataRequest +// + +@implementation GTLRSheets_DeleteDeveloperMetadataRequest +@dynamic dataFilter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteDeveloperMetadataResponse +// + +@implementation GTLRSheets_DeleteDeveloperMetadataResponse +@dynamic deletedDeveloperMetadata; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"deletedDeveloperMetadata" : [GTLRSheets_DeveloperMetadata class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteDimensionGroupRequest +// + +@implementation GTLRSheets_DeleteDimensionGroupRequest +@dynamic range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteDimensionGroupResponse +// + +@implementation GTLRSheets_DeleteDimensionGroupResponse +@dynamic dimensionGroups; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dimensionGroups" : [GTLRSheets_DimensionGroup class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteDimensionRequest +// + +@implementation GTLRSheets_DeleteDimensionRequest +@dynamic range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteEmbeddedObjectRequest +// + +@implementation GTLRSheets_DeleteEmbeddedObjectRequest +@dynamic objectId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteFilterViewRequest +// + +@implementation GTLRSheets_DeleteFilterViewRequest +@dynamic filterId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteNamedRangeRequest +// + +@implementation GTLRSheets_DeleteNamedRangeRequest +@dynamic namedRangeId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteProtectedRangeRequest +// + +@implementation GTLRSheets_DeleteProtectedRangeRequest +@dynamic protectedRangeId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteRangeRequest +// + +@implementation GTLRSheets_DeleteRangeRequest +@dynamic range, shiftDimension; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeleteSheetRequest +// + +@implementation GTLRSheets_DeleteSheetRequest +@dynamic sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeveloperMetadata +// + +@implementation GTLRSheets_DeveloperMetadata +@dynamic location, metadataId, metadataKey, metadataValue, visibility; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeveloperMetadataLocation +// + +@implementation GTLRSheets_DeveloperMetadataLocation +@dynamic dimensionRange, locationType, sheetId, spreadsheet; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DeveloperMetadataLookup +// + +@implementation GTLRSheets_DeveloperMetadataLookup +@dynamic locationMatchingStrategy, locationType, metadataId, metadataKey, + metadataLocation, metadataValue, visibility; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DimensionGroup +// + +@implementation GTLRSheets_DimensionGroup +@dynamic collapsed, depth, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DimensionProperties +// + +@implementation GTLRSheets_DimensionProperties +@dynamic developerMetadata, hiddenByFilter, hiddenByUser, pixelSize; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"developerMetadata" : [GTLRSheets_DeveloperMetadata class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DimensionRange +// + +@implementation GTLRSheets_DimensionRange +@dynamic dimension, endIndex, sheetId, startIndex; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DuplicateFilterViewRequest +// + +@implementation GTLRSheets_DuplicateFilterViewRequest +@dynamic filterId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DuplicateFilterViewResponse +// + +@implementation GTLRSheets_DuplicateFilterViewResponse +@dynamic filter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DuplicateSheetRequest +// + +@implementation GTLRSheets_DuplicateSheetRequest +@dynamic insertSheetIndex, newSheetId, newSheetName, sourceSheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_DuplicateSheetResponse +// + +@implementation GTLRSheets_DuplicateSheetResponse +@dynamic properties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Editors +// + +@implementation GTLRSheets_Editors +@dynamic domainUsersCanEdit, groups, users; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"groups" : [NSString class], + @"users" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_EmbeddedChart +// + +@implementation GTLRSheets_EmbeddedChart +@dynamic chartId, position, spec; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_EmbeddedObjectPosition +// + +@implementation GTLRSheets_EmbeddedObjectPosition +@dynamic newSheet, overlayPosition, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ErrorValue +// + +@implementation GTLRSheets_ErrorValue +@dynamic message, type; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ExtendedValue +// + +@implementation GTLRSheets_ExtendedValue +@dynamic boolValue, errorValue, formulaValue, numberValue, stringValue; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_FilterCriteria +// + +@implementation GTLRSheets_FilterCriteria +@dynamic condition, hiddenValues; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"hiddenValues" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_FilterView +// + +@implementation GTLRSheets_FilterView +@dynamic criteria, filterViewId, namedRangeId, range, sortSpecs, title; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"sortSpecs" : [GTLRSheets_SortSpec class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_FilterView_Criteria +// + +@implementation GTLRSheets_FilterView_Criteria + ++ (Class)classForAdditionalProperties { + return [GTLRSheets_FilterCriteria class]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_FindReplaceRequest +// + +@implementation GTLRSheets_FindReplaceRequest +@dynamic allSheets, find, includeFormulas, matchCase, matchEntireCell, range, + replacement, searchByRegex, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_FindReplaceResponse +// + +@implementation GTLRSheets_FindReplaceResponse +@dynamic formulasChanged, occurrencesChanged, rowsChanged, sheetsChanged, + valuesChanged; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GetSpreadsheetByDataFilterRequest +// + +@implementation GTLRSheets_GetSpreadsheetByDataFilterRequest +@dynamic dataFilters, includeGridData; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GradientRule +// + +@implementation GTLRSheets_GradientRule +@dynamic maxpoint, midpoint, minpoint; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GridCoordinate +// + +@implementation GTLRSheets_GridCoordinate +@dynamic columnIndex, rowIndex, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GridData +// + +@implementation GTLRSheets_GridData +@dynamic columnMetadata, rowData, rowMetadata, startColumn, startRow; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"columnMetadata" : [GTLRSheets_DimensionProperties class], + @"rowData" : [GTLRSheets_RowData class], + @"rowMetadata" : [GTLRSheets_DimensionProperties class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GridProperties +// + +@implementation GTLRSheets_GridProperties +@dynamic columnCount, columnGroupControlAfter, frozenColumnCount, + frozenRowCount, hideGridlines, rowCount, rowGroupControlAfter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_GridRange +// + +@implementation GTLRSheets_GridRange +@dynamic endColumnIndex, endRowIndex, sheetId, startColumnIndex, startRowIndex; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_HistogramChartSpec +// + +@implementation GTLRSheets_HistogramChartSpec +@dynamic bucketSize, legendPosition, outlierPercentile, series, + showItemDividers; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"series" : [GTLRSheets_HistogramSeries class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_HistogramRule +// + +@implementation GTLRSheets_HistogramRule +@dynamic end, interval, start; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_HistogramSeries +// + +@implementation GTLRSheets_HistogramSeries +@dynamic barColor, data; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_InsertDimensionRequest +// + +@implementation GTLRSheets_InsertDimensionRequest +@dynamic inheritFromBefore, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_InsertRangeRequest +// + +@implementation GTLRSheets_InsertRangeRequest +@dynamic range, shiftDimension; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_InterpolationPoint +// + +@implementation GTLRSheets_InterpolationPoint +@dynamic color, type, value; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_IterativeCalculationSettings +// + +@implementation GTLRSheets_IterativeCalculationSettings +@dynamic convergenceThreshold, maxIterations; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_LineStyle +// + +@implementation GTLRSheets_LineStyle +@dynamic type, width; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ManualRule +// + +@implementation GTLRSheets_ManualRule +@dynamic groups; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"groups" : [GTLRSheets_ManualRuleGroup class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ManualRuleGroup +// + +@implementation GTLRSheets_ManualRuleGroup +@dynamic groupName, items; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"items" : [GTLRSheets_ExtendedValue class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_MatchedDeveloperMetadata +// + +@implementation GTLRSheets_MatchedDeveloperMetadata +@dynamic dataFilters, developerMetadata; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_MatchedValueRange +// + +@implementation GTLRSheets_MatchedValueRange +@dynamic dataFilters, valueRange; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_MergeCellsRequest +// + +@implementation GTLRSheets_MergeCellsRequest +@dynamic mergeType, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_MoveDimensionRequest +// + +@implementation GTLRSheets_MoveDimensionRequest +@dynamic destinationIndex, source; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_NamedRange +// + +@implementation GTLRSheets_NamedRange +@dynamic name, namedRangeId, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_NumberFormat +// + +@implementation GTLRSheets_NumberFormat +@dynamic pattern, type; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_OrgChartSpec +// + +@implementation GTLRSheets_OrgChartSpec +@dynamic labels, nodeColor, nodeSize, parentLabels, selectedNodeColor, tooltips; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_OverlayPosition +// + +@implementation GTLRSheets_OverlayPosition +@dynamic anchorCell, heightPixels, offsetXPixels, offsetYPixels, widthPixels; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Padding +// + +@implementation GTLRSheets_Padding +@dynamic bottom, left, right, top; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PasteDataRequest +// + +@implementation GTLRSheets_PasteDataRequest +@dynamic coordinate, data, delimiter, html, type; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PieChartSpec +// + +@implementation GTLRSheets_PieChartSpec +@dynamic domain, legendPosition, pieHole, series, threeDimensional; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotFilterCriteria +// + +@implementation GTLRSheets_PivotFilterCriteria +@dynamic visibleValues; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"visibleValues" : [NSString class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotGroup +// + +@implementation GTLRSheets_PivotGroup +@dynamic groupRule, label, repeatHeadings, showTotals, sortOrder, + sourceColumnOffset, valueBucket, valueMetadata; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"valueMetadata" : [GTLRSheets_PivotGroupValueMetadata class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotGroupRule +// + +@implementation GTLRSheets_PivotGroupRule +@dynamic dateTimeRule, histogramRule, manualRule; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotGroupSortValueBucket +// + +@implementation GTLRSheets_PivotGroupSortValueBucket +@dynamic buckets, valuesIndex; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"buckets" : [GTLRSheets_ExtendedValue class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotGroupValueMetadata +// + +@implementation GTLRSheets_PivotGroupValueMetadata +@dynamic collapsed, value; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotTable +// + +@implementation GTLRSheets_PivotTable +@dynamic columns, criteria, rows, source, valueLayout, values; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"columns" : [GTLRSheets_PivotGroup class], + @"rows" : [GTLRSheets_PivotGroup class], + @"values" : [GTLRSheets_PivotValue class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotTable_Criteria +// + +@implementation GTLRSheets_PivotTable_Criteria + ++ (Class)classForAdditionalProperties { + return [GTLRSheets_PivotFilterCriteria class]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_PivotValue +// + +@implementation GTLRSheets_PivotValue +@dynamic calculatedDisplayType, formula, name, sourceColumnOffset, + summarizeFunction; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ProtectedRange +// + +@implementation GTLRSheets_ProtectedRange +@dynamic descriptionProperty, editors, namedRangeId, protectedRangeId, range, + requestingUserCanEdit, unprotectedRanges, warningOnly; + ++ (NSDictionary<NSString *, NSString *> *)propertyToJSONKeyMap { + return @{ @"descriptionProperty" : @"description" }; +} + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"unprotectedRanges" : [GTLRSheets_GridRange class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_RandomizeRangeRequest +// + +@implementation GTLRSheets_RandomizeRangeRequest +@dynamic range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_RepeatCellRequest +// + +@implementation GTLRSheets_RepeatCellRequest +@dynamic cell, fields, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Request +// + +@implementation GTLRSheets_Request +@dynamic addBanding, addChart, addConditionalFormatRule, addDimensionGroup, + addFilterView, addNamedRange, addProtectedRange, addSheet, appendCells, + appendDimension, autoFill, autoResizeDimensions, clearBasicFilter, + copyPaste, createDeveloperMetadata, cutPaste, deleteBanding, + deleteConditionalFormatRule, deleteDeveloperMetadata, deleteDimension, + deleteDimensionGroup, deleteEmbeddedObject, deleteFilterView, + deleteNamedRange, deleteProtectedRange, deleteRange, deleteSheet, + duplicateFilterView, duplicateSheet, findReplace, insertDimension, + insertRange, mergeCells, moveDimension, pasteData, randomizeRange, + repeatCell, setBasicFilter, setDataValidation, sortRange, + textToColumns, unmergeCells, updateBanding, updateBorders, updateCells, + updateChartSpec, updateConditionalFormatRule, updateDeveloperMetadata, + updateDimensionGroup, updateDimensionProperties, + updateEmbeddedObjectPosition, updateFilterView, updateNamedRange, + updateProtectedRange, updateSheetProperties, + updateSpreadsheetProperties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Response +// + +@implementation GTLRSheets_Response +@dynamic addBanding, addChart, addDimensionGroup, addFilterView, addNamedRange, + addProtectedRange, addSheet, createDeveloperMetadata, + deleteConditionalFormatRule, deleteDeveloperMetadata, + deleteDimensionGroup, duplicateFilterView, duplicateSheet, findReplace, + updateConditionalFormatRule, updateDeveloperMetadata, + updateEmbeddedObjectPosition; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_RowData +// + +@implementation GTLRSheets_RowData +@dynamic values; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"values" : [GTLRSheets_CellData class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SearchDeveloperMetadataRequest +// + +@implementation GTLRSheets_SearchDeveloperMetadataRequest +@dynamic dataFilters; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SearchDeveloperMetadataResponse +// + +@implementation GTLRSheets_SearchDeveloperMetadataResponse +@dynamic matchedDeveloperMetadata; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"matchedDeveloperMetadata" : [GTLRSheets_MatchedDeveloperMetadata class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SetBasicFilterRequest +// + +@implementation GTLRSheets_SetBasicFilterRequest +@dynamic filter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SetDataValidationRequest +// + +@implementation GTLRSheets_SetDataValidationRequest +@dynamic range, rule; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Sheet +// + +@implementation GTLRSheets_Sheet +@dynamic bandedRanges, basicFilter, charts, columnGroups, conditionalFormats, + data, developerMetadata, filterViews, merges, properties, + protectedRanges, rowGroups; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"bandedRanges" : [GTLRSheets_BandedRange class], + @"charts" : [GTLRSheets_EmbeddedChart class], + @"columnGroups" : [GTLRSheets_DimensionGroup class], + @"conditionalFormats" : [GTLRSheets_ConditionalFormatRule class], + @"data" : [GTLRSheets_GridData class], + @"developerMetadata" : [GTLRSheets_DeveloperMetadata class], + @"filterViews" : [GTLRSheets_FilterView class], + @"merges" : [GTLRSheets_GridRange class], + @"protectedRanges" : [GTLRSheets_ProtectedRange class], + @"rowGroups" : [GTLRSheets_DimensionGroup class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SheetProperties +// + +@implementation GTLRSheets_SheetProperties +@dynamic gridProperties, hidden, index, rightToLeft, sheetId, sheetType, + tabColor, title; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SortRangeRequest +// + +@implementation GTLRSheets_SortRangeRequest +@dynamic range, sortSpecs; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"sortSpecs" : [GTLRSheets_SortSpec class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SortSpec +// + +@implementation GTLRSheets_SortSpec +@dynamic dimensionIndex, sortOrder; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SourceAndDestination +// + +@implementation GTLRSheets_SourceAndDestination +@dynamic dimension, fillLength, source; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_Spreadsheet +// + +@implementation GTLRSheets_Spreadsheet +@dynamic developerMetadata, namedRanges, properties, sheets, spreadsheetId, + spreadsheetUrl; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"developerMetadata" : [GTLRSheets_DeveloperMetadata class], + @"namedRanges" : [GTLRSheets_NamedRange class], + @"sheets" : [GTLRSheets_Sheet class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_SpreadsheetProperties +// + +@implementation GTLRSheets_SpreadsheetProperties +@dynamic autoRecalc, defaultFormat, iterativeCalculationSettings, locale, + timeZone, title; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TextFormat +// + +@implementation GTLRSheets_TextFormat +@dynamic bold, fontFamily, fontSize, foregroundColor, italic, strikethrough, + underline; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TextFormatRun +// + +@implementation GTLRSheets_TextFormatRun +@dynamic format, startIndex; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TextPosition +// + +@implementation GTLRSheets_TextPosition +@dynamic horizontalAlignment; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TextRotation +// + +@implementation GTLRSheets_TextRotation +@dynamic angle, vertical; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TextToColumnsRequest +// + +@implementation GTLRSheets_TextToColumnsRequest +@dynamic delimiter, delimiterType, source; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TreemapChartColorScale +// + +@implementation GTLRSheets_TreemapChartColorScale +@dynamic maxValueColor, midValueColor, minValueColor, noDataColor; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_TreemapChartSpec +// + +@implementation GTLRSheets_TreemapChartSpec +@dynamic colorData, colorScale, headerColor, hideTooltips, hintedLevels, labels, + levels, maxValue, minValue, parentLabels, sizeData, textFormat; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UnmergeCellsRequest +// + +@implementation GTLRSheets_UnmergeCellsRequest +@dynamic range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateBandingRequest +// + +@implementation GTLRSheets_UpdateBandingRequest +@dynamic bandedRange, fields; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateBordersRequest +// + +@implementation GTLRSheets_UpdateBordersRequest +@dynamic bottom, innerHorizontal, innerVertical, left, range, right, top; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateCellsRequest +// + +@implementation GTLRSheets_UpdateCellsRequest +@dynamic fields, range, rows, start; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"rows" : [GTLRSheets_RowData class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateChartSpecRequest +// + +@implementation GTLRSheets_UpdateChartSpecRequest +@dynamic chartId, spec; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateConditionalFormatRuleRequest +// + +@implementation GTLRSheets_UpdateConditionalFormatRuleRequest +@dynamic index, newIndex, rule, sheetId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateConditionalFormatRuleResponse +// + +@implementation GTLRSheets_UpdateConditionalFormatRuleResponse +@dynamic newIndex, newRule, oldIndex, oldRule; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateDeveloperMetadataRequest +// + +@implementation GTLRSheets_UpdateDeveloperMetadataRequest +@dynamic dataFilters, developerMetadata, fields; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"dataFilters" : [GTLRSheets_DataFilter class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateDeveloperMetadataResponse +// + +@implementation GTLRSheets_UpdateDeveloperMetadataResponse +@dynamic developerMetadata; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"developerMetadata" : [GTLRSheets_DeveloperMetadata class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateDimensionGroupRequest +// + +@implementation GTLRSheets_UpdateDimensionGroupRequest +@dynamic dimensionGroup, fields; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateDimensionPropertiesRequest +// + +@implementation GTLRSheets_UpdateDimensionPropertiesRequest +@dynamic fields, properties, range; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateEmbeddedObjectPositionRequest +// + +@implementation GTLRSheets_UpdateEmbeddedObjectPositionRequest +@dynamic fields, newPosition, objectId; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateEmbeddedObjectPositionResponse +// + +@implementation GTLRSheets_UpdateEmbeddedObjectPositionResponse +@dynamic position; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateFilterViewRequest +// + +@implementation GTLRSheets_UpdateFilterViewRequest +@dynamic fields, filter; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateNamedRangeRequest +// + +@implementation GTLRSheets_UpdateNamedRangeRequest +@dynamic fields, namedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateProtectedRangeRequest +// + +@implementation GTLRSheets_UpdateProtectedRangeRequest +@dynamic fields, protectedRange; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateSheetPropertiesRequest +// + +@implementation GTLRSheets_UpdateSheetPropertiesRequest +@dynamic fields, properties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateSpreadsheetPropertiesRequest +// + +@implementation GTLRSheets_UpdateSpreadsheetPropertiesRequest +@dynamic fields, properties; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateValuesByDataFilterResponse +// + +@implementation GTLRSheets_UpdateValuesByDataFilterResponse +@dynamic dataFilter, updatedCells, updatedColumns, updatedData, updatedRange, + updatedRows; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_UpdateValuesResponse +// + +@implementation GTLRSheets_UpdateValuesResponse +@dynamic spreadsheetId, updatedCells, updatedColumns, updatedData, updatedRange, + updatedRows; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_ValueRange +// + +@implementation GTLRSheets_ValueRange +@dynamic majorDimension, range, values; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"values" : [NSObject class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_WaterfallChartColumnStyle +// + +@implementation GTLRSheets_WaterfallChartColumnStyle +@dynamic color, label; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_WaterfallChartCustomSubtotal +// + +@implementation GTLRSheets_WaterfallChartCustomSubtotal +@dynamic dataIsSubtotal, label, subtotalIndex; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_WaterfallChartDomain +// + +@implementation GTLRSheets_WaterfallChartDomain +@dynamic data, reversed; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_WaterfallChartSeries +// + +@implementation GTLRSheets_WaterfallChartSeries +@dynamic customSubtotals, data, hideTrailingSubtotal, negativeColumnsStyle, + positiveColumnsStyle, subtotalColumnsStyle; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"customSubtotals" : [GTLRSheets_WaterfallChartCustomSubtotal class] + }; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLRSheets_WaterfallChartSpec +// + +@implementation GTLRSheets_WaterfallChartSpec +@dynamic connectorLineStyle, domain, firstValueIsTotal, hideConnectorLines, + series, stackedType; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"series" : [GTLRSheets_WaterfallChartSeries class] + }; + return map; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.h b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.h @@ -0,0 +1,1078 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#if GTLR_BUILT_AS_FRAMEWORK + #import "GTLR/GTLRQuery.h" +#else + #import "GTLRQuery.h" +#endif + +#if GTLR_RUNTIME_VERSION != 3000 +#error This file was generated by a different version of ServiceGenerator which is incompatible with this GTLR library source. +#endif + +@class GTLRSheets_BatchClearValuesByDataFilterRequest; +@class GTLRSheets_BatchClearValuesRequest; +@class GTLRSheets_BatchGetValuesByDataFilterRequest; +@class GTLRSheets_BatchUpdateSpreadsheetRequest; +@class GTLRSheets_BatchUpdateValuesByDataFilterRequest; +@class GTLRSheets_BatchUpdateValuesRequest; +@class GTLRSheets_ClearValuesRequest; +@class GTLRSheets_CopySheetToAnotherSpreadsheetRequest; +@class GTLRSheets_GetSpreadsheetByDataFilterRequest; +@class GTLRSheets_SearchDeveloperMetadataRequest; +@class GTLRSheets_Spreadsheet; +@class GTLRSheets_ValueRange; + +// Generated comments include content from the discovery document; avoid them +// causing warnings since clang's checks are some what arbitrary. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +NS_ASSUME_NONNULL_BEGIN + +// ---------------------------------------------------------------------------- +// Constants - For some of the query classes' properties below. + +// ---------------------------------------------------------------------------- +// dateTimeRenderOption + +/** Value: "FORMATTED_STRING" */ +GTLR_EXTERN NSString * const kGTLRSheetsDateTimeRenderOptionFormattedString; +/** Value: "SERIAL_NUMBER" */ +GTLR_EXTERN NSString * const kGTLRSheetsDateTimeRenderOptionSerialNumber; + +// ---------------------------------------------------------------------------- +// insertDataOption + +/** Value: "INSERT_ROWS" */ +GTLR_EXTERN NSString * const kGTLRSheetsInsertDataOptionInsertRows; +/** Value: "OVERWRITE" */ +GTLR_EXTERN NSString * const kGTLRSheetsInsertDataOptionOverwrite; + +// ---------------------------------------------------------------------------- +// majorDimension + +/** Value: "COLUMNS" */ +GTLR_EXTERN NSString * const kGTLRSheetsMajorDimensionColumns; +/** Value: "DIMENSION_UNSPECIFIED" */ +GTLR_EXTERN NSString * const kGTLRSheetsMajorDimensionDimensionUnspecified; +/** Value: "ROWS" */ +GTLR_EXTERN NSString * const kGTLRSheetsMajorDimensionRows; + +// ---------------------------------------------------------------------------- +// responseDateTimeRenderOption + +/** Value: "FORMATTED_STRING" */ +GTLR_EXTERN NSString * const kGTLRSheetsResponseDateTimeRenderOptionFormattedString; +/** Value: "SERIAL_NUMBER" */ +GTLR_EXTERN NSString * const kGTLRSheetsResponseDateTimeRenderOptionSerialNumber; + +// ---------------------------------------------------------------------------- +// responseValueRenderOption + +/** Value: "FORMATTED_VALUE" */ +GTLR_EXTERN NSString * const kGTLRSheetsResponseValueRenderOptionFormattedValue; +/** Value: "FORMULA" */ +GTLR_EXTERN NSString * const kGTLRSheetsResponseValueRenderOptionFormula; +/** Value: "UNFORMATTED_VALUE" */ +GTLR_EXTERN NSString * const kGTLRSheetsResponseValueRenderOptionUnformattedValue; + +// ---------------------------------------------------------------------------- +// valueInputOption + +/** Value: "INPUT_VALUE_OPTION_UNSPECIFIED" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueInputOptionInputValueOptionUnspecified; +/** Value: "RAW" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueInputOptionRaw; +/** Value: "USER_ENTERED" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueInputOptionUserEntered; + +// ---------------------------------------------------------------------------- +// valueRenderOption + +/** Value: "FORMATTED_VALUE" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueRenderOptionFormattedValue; +/** Value: "FORMULA" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueRenderOptionFormula; +/** Value: "UNFORMATTED_VALUE" */ +GTLR_EXTERN NSString * const kGTLRSheetsValueRenderOptionUnformattedValue; + +// ---------------------------------------------------------------------------- +// Query Classes +// + +/** + * Parent class for other Sheets query classes. + */ +@interface GTLRSheetsQuery : GTLRQuery + +/** Selector specifying which fields to include in a partial response. */ +@property(nonatomic, copy, nullable) NSString *fields; + +@end + +/** + * Applies one or more updates to the spreadsheet. + * Each request is validated before + * being applied. If any request is not valid then the entire request will + * fail and nothing will be applied. + * Some requests have replies to + * give you some information about how + * they are applied. The replies will mirror the requests. For example, + * if you applied 4 updates and the 3rd one had a reply, then the + * response will have 2 empty replies, the actual reply, and another empty + * reply, in that order. + * Due to the collaborative nature of spreadsheets, it is not guaranteed that + * the spreadsheet will reflect exactly your changes after this completes, + * however it is guaranteed that the updates in the request will be + * applied together atomically. Your changes may be altered with respect to + * collaborator changes. If there are no collaborators, the spreadsheet + * should reflect your changes. + * + * Method: sheets.spreadsheets.batchUpdate + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsBatchUpdate : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsBatchUpdateWithObject:spreadsheetId:] + +/** The spreadsheet to apply the updates to. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchUpdateSpreadsheetResponse. + * + * Applies one or more updates to the spreadsheet. + * Each request is validated before + * being applied. If any request is not valid then the entire request will + * fail and nothing will be applied. + * Some requests have replies to + * give you some information about how + * they are applied. The replies will mirror the requests. For example, + * if you applied 4 updates and the 3rd one had a reply, then the + * response will have 2 empty replies, the actual reply, and another empty + * reply, in that order. + * Due to the collaborative nature of spreadsheets, it is not guaranteed that + * the spreadsheet will reflect exactly your changes after this completes, + * however it is guaranteed that the updates in the request will be + * applied together atomically. Your changes may be altered with respect to + * collaborator changes. If there are no collaborators, the spreadsheet + * should reflect your changes. + * + * @param object The @c GTLRSheets_BatchUpdateSpreadsheetRequest to include in + * the query. + * @param spreadsheetId The spreadsheet to apply the updates to. + * + * @return GTLRSheetsQuery_SpreadsheetsBatchUpdate + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateSpreadsheetRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Creates a spreadsheet, returning the newly created spreadsheet. + * + * Method: sheets.spreadsheets.create + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsCreate : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsCreateWithObject:] + +/** + * Fetches a @c GTLRSheets_Spreadsheet. + * + * Creates a spreadsheet, returning the newly created spreadsheet. + * + * @param object The @c GTLRSheets_Spreadsheet to include in the query. + * + * @return GTLRSheetsQuery_SpreadsheetsCreate + */ ++ (instancetype)queryWithObject:(GTLRSheets_Spreadsheet *)object; + +@end + +/** + * Returns the developer metadata with the specified ID. + * The caller must specify the spreadsheet ID and the developer metadata's + * unique metadataId. + * + * Method: sheets.spreadsheets.developerMetadata.get + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsDeveloperMetadataGet : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsDeveloperMetadataGetWithspreadsheetId:metadataId:] + +/** The ID of the developer metadata to retrieve. */ +@property(nonatomic, assign) NSInteger metadataId; + +/** The ID of the spreadsheet to retrieve metadata from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_DeveloperMetadata. + * + * Returns the developer metadata with the specified ID. + * The caller must specify the spreadsheet ID and the developer metadata's + * unique metadataId. + * + * @param spreadsheetId The ID of the spreadsheet to retrieve metadata from. + * @param metadataId The ID of the developer metadata to retrieve. + * + * @return GTLRSheetsQuery_SpreadsheetsDeveloperMetadataGet + */ ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId + metadataId:(NSInteger)metadataId; + +@end + +/** + * Returns all developer metadata matching the specified DataFilter. + * If the provided DataFilter represents a DeveloperMetadataLookup object, + * this will return all DeveloperMetadata entries selected by it. If the + * DataFilter represents a location in a spreadsheet, this will return all + * developer metadata associated with locations intersecting that region. + * + * Method: sheets.spreadsheets.developerMetadata.search + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsDeveloperMetadataSearch : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsDeveloperMetadataSearchWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to retrieve metadata from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_SearchDeveloperMetadataResponse. + * + * Returns all developer metadata matching the specified DataFilter. + * If the provided DataFilter represents a DeveloperMetadataLookup object, + * this will return all DeveloperMetadata entries selected by it. If the + * DataFilter represents a location in a spreadsheet, this will return all + * developer metadata associated with locations intersecting that region. + * + * @param object The @c GTLRSheets_SearchDeveloperMetadataRequest to include in + * the query. + * @param spreadsheetId The ID of the spreadsheet to retrieve metadata from. + * + * @return GTLRSheetsQuery_SpreadsheetsDeveloperMetadataSearch + */ ++ (instancetype)queryWithObject:(GTLRSheets_SearchDeveloperMetadataRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Returns the spreadsheet at the given ID. + * The caller must specify the spreadsheet ID. + * By default, data within grids will not be returned. + * You can include grid data one of two ways: + * * Specify a field mask listing your desired fields using the `fields` URL + * parameter in HTTP + * * Set the includeGridData + * URL parameter to true. If a field mask is set, the `includeGridData` + * parameter is ignored + * For large spreadsheets, it is recommended to retrieve only the specific + * fields of the spreadsheet that you want. + * To retrieve only subsets of the spreadsheet, use the + * ranges URL parameter. + * Multiple ranges can be specified. Limiting the range will + * return only the portions of the spreadsheet that intersect the requested + * ranges. Ranges are specified using A1 notation. + * + * Method: sheets.spreadsheets.get + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsDriveReadonly + * @c kGTLRAuthScopeSheetsSpreadsheets + * @c kGTLRAuthScopeSheetsSpreadsheetsReadonly + */ +@interface GTLRSheetsQuery_SpreadsheetsGet : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsGetWithspreadsheetId:] + +/** + * True if grid data should be returned. + * This parameter is ignored if a field mask was set in the request. + */ +@property(nonatomic, assign) BOOL includeGridData; + +/** The ranges to retrieve from the spreadsheet. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *ranges; + +/** The spreadsheet to request. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_Spreadsheet. + * + * Returns the spreadsheet at the given ID. + * The caller must specify the spreadsheet ID. + * By default, data within grids will not be returned. + * You can include grid data one of two ways: + * * Specify a field mask listing your desired fields using the `fields` URL + * parameter in HTTP + * * Set the includeGridData + * URL parameter to true. If a field mask is set, the `includeGridData` + * parameter is ignored + * For large spreadsheets, it is recommended to retrieve only the specific + * fields of the spreadsheet that you want. + * To retrieve only subsets of the spreadsheet, use the + * ranges URL parameter. + * Multiple ranges can be specified. Limiting the range will + * return only the portions of the spreadsheet that intersect the requested + * ranges. Ranges are specified using A1 notation. + * + * @param spreadsheetId The spreadsheet to request. + * + * @return GTLRSheetsQuery_SpreadsheetsGet + */ ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Returns the spreadsheet at the given ID. + * The caller must specify the spreadsheet ID. + * This method differs from GetSpreadsheet in that it allows selecting + * which subsets of spreadsheet data to return by specifying a + * dataFilters parameter. + * Multiple DataFilters can be specified. Specifying one or + * more data filters will return the portions of the spreadsheet that + * intersect ranges matched by any of the filters. + * By default, data within grids will not be returned. + * You can include grid data one of two ways: + * * Specify a field mask listing your desired fields using the `fields` URL + * parameter in HTTP + * * Set the includeGridData + * parameter to true. If a field mask is set, the `includeGridData` + * parameter is ignored + * For large spreadsheets, it is recommended to retrieve only the specific + * fields of the spreadsheet that you want. + * + * Method: sheets.spreadsheets.getByDataFilter + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsGetByDataFilter : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsGetByDataFilterWithObject:spreadsheetId:] + +/** The spreadsheet to request. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_Spreadsheet. + * + * Returns the spreadsheet at the given ID. + * The caller must specify the spreadsheet ID. + * This method differs from GetSpreadsheet in that it allows selecting + * which subsets of spreadsheet data to return by specifying a + * dataFilters parameter. + * Multiple DataFilters can be specified. Specifying one or + * more data filters will return the portions of the spreadsheet that + * intersect ranges matched by any of the filters. + * By default, data within grids will not be returned. + * You can include grid data one of two ways: + * * Specify a field mask listing your desired fields using the `fields` URL + * parameter in HTTP + * * Set the includeGridData + * parameter to true. If a field mask is set, the `includeGridData` + * parameter is ignored + * For large spreadsheets, it is recommended to retrieve only the specific + * fields of the spreadsheet that you want. + * + * @param object The @c GTLRSheets_GetSpreadsheetByDataFilterRequest to include + * in the query. + * @param spreadsheetId The spreadsheet to request. + * + * @return GTLRSheetsQuery_SpreadsheetsGetByDataFilter + */ ++ (instancetype)queryWithObject:(GTLRSheets_GetSpreadsheetByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Copies a single sheet from a spreadsheet to another spreadsheet. + * Returns the properties of the newly created sheet. + * + * Method: sheets.spreadsheets.sheets.copyTo + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsSheetsCopyTo : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsSheetsCopyToWithObject:spreadsheetId:sheetId:] + +/** The ID of the sheet to copy. */ +@property(nonatomic, assign) NSInteger sheetId; + +/** The ID of the spreadsheet containing the sheet to copy. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_SheetProperties. + * + * Copies a single sheet from a spreadsheet to another spreadsheet. + * Returns the properties of the newly created sheet. + * + * @param object The @c GTLRSheets_CopySheetToAnotherSpreadsheetRequest to + * include in the query. + * @param spreadsheetId The ID of the spreadsheet containing the sheet to copy. + * @param sheetId The ID of the sheet to copy. + * + * @return GTLRSheetsQuery_SpreadsheetsSheetsCopyTo + */ ++ (instancetype)queryWithObject:(GTLRSheets_CopySheetToAnotherSpreadsheetRequest *)object + spreadsheetId:(NSString *)spreadsheetId + sheetId:(NSInteger)sheetId; + +@end + +/** + * Appends values to a spreadsheet. The input range is used to search for + * existing data and find a "table" within that range. Values will be + * appended to the next row of the table, starting with the first column of + * the table. See the + * [guide](/sheets/api/guides/values#appending_values) + * and + * [sample code](/sheets/api/samples/writing#append_values) + * for specific details of how tables are detected and data is appended. + * The caller must specify the spreadsheet ID, range, and + * a valueInputOption. The `valueInputOption` only + * controls how the input data will be added to the sheet (column-wise or + * row-wise), it does not influence what cell the data starts being written + * to. + * + * Method: sheets.spreadsheets.values.append + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesAppend : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesAppendWithObject:spreadsheetId:range:] + +/** + * Determines if the update response should include the values + * of the cells that were appended. By default, responses + * do not include the updated values. + */ +@property(nonatomic, assign) BOOL includeValuesInResponse; + +/** + * How the input data should be inserted. + * + * Likely values: + * @arg @c kGTLRSheetsInsertDataOptionOverwrite Value "OVERWRITE" + * @arg @c kGTLRSheetsInsertDataOptionInsertRows Value "INSERT_ROWS" + */ +@property(nonatomic, copy, nullable) NSString *insertDataOption; + +/** + * The A1 notation of a range to search for a logical table of data. + * Values will be appended after the last row of the table. + */ +@property(nonatomic, copy, nullable) NSString *range; + +/** + * Determines how dates, times, and durations in the response should be + * rendered. This is ignored if response_value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is [DateTimeRenderOption.SERIAL_NUMBER]. + * + * Likely values: + * @arg @c kGTLRSheetsResponseDateTimeRenderOptionSerialNumber Value + * "SERIAL_NUMBER" + * @arg @c kGTLRSheetsResponseDateTimeRenderOptionFormattedString Value + * "FORMATTED_STRING" + */ +@property(nonatomic, copy, nullable) NSString *responseDateTimeRenderOption; + +/** + * Determines how values in the response should be rendered. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheetsResponseValueRenderOptionFormattedValue Value + * "FORMATTED_VALUE" + * @arg @c kGTLRSheetsResponseValueRenderOptionUnformattedValue Value + * "UNFORMATTED_VALUE" + * @arg @c kGTLRSheetsResponseValueRenderOptionFormula Value "FORMULA" + */ +@property(nonatomic, copy, nullable) NSString *responseValueRenderOption; + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * How the input data should be interpreted. + * + * Likely values: + * @arg @c kGTLRSheetsValueInputOptionInputValueOptionUnspecified Value + * "INPUT_VALUE_OPTION_UNSPECIFIED" + * @arg @c kGTLRSheetsValueInputOptionRaw Value "RAW" + * @arg @c kGTLRSheetsValueInputOptionUserEntered Value "USER_ENTERED" + */ +@property(nonatomic, copy, nullable) NSString *valueInputOption; + +/** + * Fetches a @c GTLRSheets_AppendValuesResponse. + * + * Appends values to a spreadsheet. The input range is used to search for + * existing data and find a "table" within that range. Values will be + * appended to the next row of the table, starting with the first column of + * the table. See the + * [guide](/sheets/api/guides/values#appending_values) + * and + * [sample code](/sheets/api/samples/writing#append_values) + * for specific details of how tables are detected and data is appended. + * The caller must specify the spreadsheet ID, range, and + * a valueInputOption. The `valueInputOption` only + * controls how the input data will be added to the sheet (column-wise or + * row-wise), it does not influence what cell the data starts being written + * to. + * + * @param object The @c GTLRSheets_ValueRange to include in the query. + * @param spreadsheetId The ID of the spreadsheet to update. + * @param range The A1 notation of a range to search for a logical table of + * data. + * Values will be appended after the last row of the table. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesAppend + */ ++ (instancetype)queryWithObject:(GTLRSheets_ValueRange *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range; + +@end + +/** + * Clears one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more ranges. + * Only values are cleared -- all other properties of the cell (such as + * formatting, data validation, etc..) are kept. + * + * Method: sheets.spreadsheets.values.batchClear + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchClear : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchClearWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchClearValuesResponse. + * + * Clears one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more ranges. + * Only values are cleared -- all other properties of the cell (such as + * formatting, data validation, etc..) are kept. + * + * @param object The @c GTLRSheets_BatchClearValuesRequest to include in the + * query. + * @param spreadsheetId The ID of the spreadsheet to update. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchClear + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchClearValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Clears one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more + * DataFilters. Ranges matching any of the specified data + * filters will be cleared. Only values are cleared -- all other properties + * of the cell (such as formatting, data validation, etc..) are kept. + * + * Method: sheets.spreadsheets.values.batchClearByDataFilter + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchClearByDataFilter : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchClearByDataFilterWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchClearValuesByDataFilterResponse. + * + * Clears one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more + * DataFilters. Ranges matching any of the specified data + * filters will be cleared. Only values are cleared -- all other properties + * of the cell (such as formatting, data validation, etc..) are kept. + * + * @param object The @c GTLRSheets_BatchClearValuesByDataFilterRequest to + * include in the query. + * @param spreadsheetId The ID of the spreadsheet to update. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchClearByDataFilter + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchClearValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Returns one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more ranges. + * + * Method: sheets.spreadsheets.values.batchGet + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsDriveReadonly + * @c kGTLRAuthScopeSheetsSpreadsheets + * @c kGTLRAuthScopeSheetsSpreadsheetsReadonly + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchGet : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchGetWithspreadsheetId:] + +/** + * How dates, times, and durations should be represented in the output. + * This is ignored if value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is [DateTimeRenderOption.SERIAL_NUMBER]. + * + * Likely values: + * @arg @c kGTLRSheetsDateTimeRenderOptionSerialNumber Value "SERIAL_NUMBER" + * @arg @c kGTLRSheetsDateTimeRenderOptionFormattedString Value + * "FORMATTED_STRING" + */ +@property(nonatomic, copy, nullable) NSString *dateTimeRenderOption; + +/** + * The major dimension that results should use. + * For example, if the spreadsheet data is: `A1=1,B1=2,A2=3,B2=4`, + * then requesting `range=A1:B2,majorDimension=ROWS` will return + * `[[1,2],[3,4]]`, + * whereas requesting `range=A1:B2,majorDimension=COLUMNS` will return + * `[[1,3],[2,4]]`. + * + * Likely values: + * @arg @c kGTLRSheetsMajorDimensionDimensionUnspecified Value + * "DIMENSION_UNSPECIFIED" + * @arg @c kGTLRSheetsMajorDimensionRows Value "ROWS" + * @arg @c kGTLRSheetsMajorDimensionColumns Value "COLUMNS" + */ +@property(nonatomic, copy, nullable) NSString *majorDimension; + +/** The A1 notation of the values to retrieve. */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *ranges; + +/** The ID of the spreadsheet to retrieve data from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * How values should be represented in the output. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheetsValueRenderOptionFormattedValue Value "FORMATTED_VALUE" + * @arg @c kGTLRSheetsValueRenderOptionUnformattedValue Value + * "UNFORMATTED_VALUE" + * @arg @c kGTLRSheetsValueRenderOptionFormula Value "FORMULA" + */ +@property(nonatomic, copy, nullable) NSString *valueRenderOption; + +/** + * Fetches a @c GTLRSheets_BatchGetValuesResponse. + * + * Returns one or more ranges of values from a spreadsheet. + * The caller must specify the spreadsheet ID and one or more ranges. + * + * @param spreadsheetId The ID of the spreadsheet to retrieve data from. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchGet + */ ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Returns one or more ranges of values that match the specified data filters. + * The caller must specify the spreadsheet ID and one or more + * DataFilters. Ranges that match any of the data filters in + * the request will be returned. + * + * Method: sheets.spreadsheets.values.batchGetByDataFilter + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchGetByDataFilter : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchGetByDataFilterWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to retrieve data from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchGetValuesByDataFilterResponse. + * + * Returns one or more ranges of values that match the specified data filters. + * The caller must specify the spreadsheet ID and one or more + * DataFilters. Ranges that match any of the data filters in + * the request will be returned. + * + * @param object The @c GTLRSheets_BatchGetValuesByDataFilterRequest to include + * in the query. + * @param spreadsheetId The ID of the spreadsheet to retrieve data from. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchGetByDataFilter + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchGetValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Sets values in one or more ranges of a spreadsheet. + * The caller must specify the spreadsheet ID, + * a valueInputOption, and one or more + * ValueRanges. + * + * Method: sheets.spreadsheets.values.batchUpdate + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchUpdate : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchUpdateWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchUpdateValuesResponse. + * + * Sets values in one or more ranges of a spreadsheet. + * The caller must specify the spreadsheet ID, + * a valueInputOption, and one or more + * ValueRanges. + * + * @param object The @c GTLRSheets_BatchUpdateValuesRequest to include in the + * query. + * @param spreadsheetId The ID of the spreadsheet to update. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchUpdate + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Sets values in one or more ranges of a spreadsheet. + * The caller must specify the spreadsheet ID, + * a valueInputOption, and one or more + * DataFilterValueRanges. + * + * Method: sheets.spreadsheets.values.batchUpdateByDataFilter + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesBatchUpdateByDataFilter : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesBatchUpdateByDataFilterWithObject:spreadsheetId:] + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_BatchUpdateValuesByDataFilterResponse. + * + * Sets values in one or more ranges of a spreadsheet. + * The caller must specify the spreadsheet ID, + * a valueInputOption, and one or more + * DataFilterValueRanges. + * + * @param object The @c GTLRSheets_BatchUpdateValuesByDataFilterRequest to + * include in the query. + * @param spreadsheetId The ID of the spreadsheet to update. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesBatchUpdateByDataFilter + */ ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId; + +@end + +/** + * Clears values from a spreadsheet. + * The caller must specify the spreadsheet ID and range. + * Only values are cleared -- all other properties of the cell (such as + * formatting, data validation, etc..) are kept. + * + * Method: sheets.spreadsheets.values.clear + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesClear : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesClearWithObject:spreadsheetId:range:] + +/** The A1 notation of the values to clear. */ +@property(nonatomic, copy, nullable) NSString *range; + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * Fetches a @c GTLRSheets_ClearValuesResponse. + * + * Clears values from a spreadsheet. + * The caller must specify the spreadsheet ID and range. + * Only values are cleared -- all other properties of the cell (such as + * formatting, data validation, etc..) are kept. + * + * @param object The @c GTLRSheets_ClearValuesRequest to include in the query. + * @param spreadsheetId The ID of the spreadsheet to update. + * @param range The A1 notation of the values to clear. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesClear + */ ++ (instancetype)queryWithObject:(GTLRSheets_ClearValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range; + +@end + +/** + * Returns a range of values from a spreadsheet. + * The caller must specify the spreadsheet ID and a range. + * + * Method: sheets.spreadsheets.values.get + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsDriveReadonly + * @c kGTLRAuthScopeSheetsSpreadsheets + * @c kGTLRAuthScopeSheetsSpreadsheetsReadonly + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesGet : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesGetWithspreadsheetId:range:] + +/** + * How dates, times, and durations should be represented in the output. + * This is ignored if value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is [DateTimeRenderOption.SERIAL_NUMBER]. + * + * Likely values: + * @arg @c kGTLRSheetsDateTimeRenderOptionSerialNumber Value "SERIAL_NUMBER" + * @arg @c kGTLRSheetsDateTimeRenderOptionFormattedString Value + * "FORMATTED_STRING" + */ +@property(nonatomic, copy, nullable) NSString *dateTimeRenderOption; + +/** + * The major dimension that results should use. + * For example, if the spreadsheet data is: `A1=1,B1=2,A2=3,B2=4`, + * then requesting `range=A1:B2,majorDimension=ROWS` will return + * `[[1,2],[3,4]]`, + * whereas requesting `range=A1:B2,majorDimension=COLUMNS` will return + * `[[1,3],[2,4]]`. + * + * Likely values: + * @arg @c kGTLRSheetsMajorDimensionDimensionUnspecified Value + * "DIMENSION_UNSPECIFIED" + * @arg @c kGTLRSheetsMajorDimensionRows Value "ROWS" + * @arg @c kGTLRSheetsMajorDimensionColumns Value "COLUMNS" + */ +@property(nonatomic, copy, nullable) NSString *majorDimension; + +/** The A1 notation of the values to retrieve. */ +@property(nonatomic, copy, nullable) NSString *range; + +/** The ID of the spreadsheet to retrieve data from. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * How values should be represented in the output. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheetsValueRenderOptionFormattedValue Value "FORMATTED_VALUE" + * @arg @c kGTLRSheetsValueRenderOptionUnformattedValue Value + * "UNFORMATTED_VALUE" + * @arg @c kGTLRSheetsValueRenderOptionFormula Value "FORMULA" + */ +@property(nonatomic, copy, nullable) NSString *valueRenderOption; + +/** + * Fetches a @c GTLRSheets_ValueRange. + * + * Returns a range of values from a spreadsheet. + * The caller must specify the spreadsheet ID and a range. + * + * @param spreadsheetId The ID of the spreadsheet to retrieve data from. + * @param range The A1 notation of the values to retrieve. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesGet + */ ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range; + +@end + +/** + * Sets values in a range of a spreadsheet. + * The caller must specify the spreadsheet ID, range, and + * a valueInputOption. + * + * Method: sheets.spreadsheets.values.update + * + * Authorization scope(s): + * @c kGTLRAuthScopeSheetsDrive + * @c kGTLRAuthScopeSheetsDriveFile + * @c kGTLRAuthScopeSheetsSpreadsheets + */ +@interface GTLRSheetsQuery_SpreadsheetsValuesUpdate : GTLRSheetsQuery +// Previous library name was +// +[GTLQuerySheets queryForSpreadsheetsValuesUpdateWithObject:spreadsheetId:range:] + +/** + * Determines if the update response should include the values + * of the cells that were updated. By default, responses + * do not include the updated values. + * If the range to write was larger than than the range actually written, + * the response will include all values in the requested range (excluding + * trailing empty rows and columns). + */ +@property(nonatomic, assign) BOOL includeValuesInResponse; + +/** The A1 notation of the values to update. */ +@property(nonatomic, copy, nullable) NSString *range; + +/** + * Determines how dates, times, and durations in the response should be + * rendered. This is ignored if response_value_render_option is + * FORMATTED_VALUE. + * The default dateTime render option is + * DateTimeRenderOption.SERIAL_NUMBER. + * + * Likely values: + * @arg @c kGTLRSheetsResponseDateTimeRenderOptionSerialNumber Value + * "SERIAL_NUMBER" + * @arg @c kGTLRSheetsResponseDateTimeRenderOptionFormattedString Value + * "FORMATTED_STRING" + */ +@property(nonatomic, copy, nullable) NSString *responseDateTimeRenderOption; + +/** + * Determines how values in the response should be rendered. + * The default render option is ValueRenderOption.FORMATTED_VALUE. + * + * Likely values: + * @arg @c kGTLRSheetsResponseValueRenderOptionFormattedValue Value + * "FORMATTED_VALUE" + * @arg @c kGTLRSheetsResponseValueRenderOptionUnformattedValue Value + * "UNFORMATTED_VALUE" + * @arg @c kGTLRSheetsResponseValueRenderOptionFormula Value "FORMULA" + */ +@property(nonatomic, copy, nullable) NSString *responseValueRenderOption; + +/** The ID of the spreadsheet to update. */ +@property(nonatomic, copy, nullable) NSString *spreadsheetId; + +/** + * How the input data should be interpreted. + * + * Likely values: + * @arg @c kGTLRSheetsValueInputOptionInputValueOptionUnspecified Value + * "INPUT_VALUE_OPTION_UNSPECIFIED" + * @arg @c kGTLRSheetsValueInputOptionRaw Value "RAW" + * @arg @c kGTLRSheetsValueInputOptionUserEntered Value "USER_ENTERED" + */ +@property(nonatomic, copy, nullable) NSString *valueInputOption; + +/** + * Fetches a @c GTLRSheets_UpdateValuesResponse. + * + * Sets values in a range of a spreadsheet. + * The caller must specify the spreadsheet ID, range, and + * a valueInputOption. + * + * @param object The @c GTLRSheets_ValueRange to include in the query. + * @param spreadsheetId The ID of the spreadsheet to update. + * @param range The A1 notation of the values to update. + * + * @return GTLRSheetsQuery_SpreadsheetsValuesUpdate + */ ++ (instancetype)queryWithObject:(GTLRSheets_ValueRange *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range; + +@end + +NS_ASSUME_NONNULL_END + +#pragma clang diagnostic pop diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.m b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsQuery.m @@ -0,0 +1,497 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#import "GTLRSheetsQuery.h" + +#import "GTLRSheetsObjects.h" + +// ---------------------------------------------------------------------------- +// Constants + +// dateTimeRenderOption +NSString * const kGTLRSheetsDateTimeRenderOptionFormattedString = @"FORMATTED_STRING"; +NSString * const kGTLRSheetsDateTimeRenderOptionSerialNumber = @"SERIAL_NUMBER"; + +// insertDataOption +NSString * const kGTLRSheetsInsertDataOptionInsertRows = @"INSERT_ROWS"; +NSString * const kGTLRSheetsInsertDataOptionOverwrite = @"OVERWRITE"; + +// majorDimension +NSString * const kGTLRSheetsMajorDimensionColumns = @"COLUMNS"; +NSString * const kGTLRSheetsMajorDimensionDimensionUnspecified = @"DIMENSION_UNSPECIFIED"; +NSString * const kGTLRSheetsMajorDimensionRows = @"ROWS"; + +// responseDateTimeRenderOption +NSString * const kGTLRSheetsResponseDateTimeRenderOptionFormattedString = @"FORMATTED_STRING"; +NSString * const kGTLRSheetsResponseDateTimeRenderOptionSerialNumber = @"SERIAL_NUMBER"; + +// responseValueRenderOption +NSString * const kGTLRSheetsResponseValueRenderOptionFormattedValue = @"FORMATTED_VALUE"; +NSString * const kGTLRSheetsResponseValueRenderOptionFormula = @"FORMULA"; +NSString * const kGTLRSheetsResponseValueRenderOptionUnformattedValue = @"UNFORMATTED_VALUE"; + +// valueInputOption +NSString * const kGTLRSheetsValueInputOptionInputValueOptionUnspecified = @"INPUT_VALUE_OPTION_UNSPECIFIED"; +NSString * const kGTLRSheetsValueInputOptionRaw = @"RAW"; +NSString * const kGTLRSheetsValueInputOptionUserEntered = @"USER_ENTERED"; + +// valueRenderOption +NSString * const kGTLRSheetsValueRenderOptionFormattedValue = @"FORMATTED_VALUE"; +NSString * const kGTLRSheetsValueRenderOptionFormula = @"FORMULA"; +NSString * const kGTLRSheetsValueRenderOptionUnformattedValue = @"UNFORMATTED_VALUE"; + +// ---------------------------------------------------------------------------- +// Query Classes +// + +@implementation GTLRSheetsQuery + +@dynamic fields; + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsBatchUpdate + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateSpreadsheetRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}:batchUpdate"; + GTLRSheetsQuery_SpreadsheetsBatchUpdate *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchUpdateSpreadsheetResponse class]; + query.loggingName = @"sheets.spreadsheets.batchUpdate"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsCreate + ++ (instancetype)queryWithObject:(GTLRSheets_Spreadsheet *)object { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSString *pathURITemplate = @"v4/spreadsheets"; + GTLRSheetsQuery_SpreadsheetsCreate *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:nil]; + query.bodyObject = object; + query.expectedObjectClass = [GTLRSheets_Spreadsheet class]; + query.loggingName = @"sheets.spreadsheets.create"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsDeveloperMetadataGet + +@dynamic metadataId, spreadsheetId; + ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId + metadataId:(NSInteger)metadataId { + NSArray *pathParams = @[ + @"metadataId", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/developerMetadata/{metadataId}"; + GTLRSheetsQuery_SpreadsheetsDeveloperMetadataGet *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:nil + pathParameterNames:pathParams]; + query.spreadsheetId = spreadsheetId; + query.metadataId = metadataId; + query.expectedObjectClass = [GTLRSheets_DeveloperMetadata class]; + query.loggingName = @"sheets.spreadsheets.developerMetadata.get"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsDeveloperMetadataSearch + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_SearchDeveloperMetadataRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/developerMetadata:search"; + GTLRSheetsQuery_SpreadsheetsDeveloperMetadataSearch *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_SearchDeveloperMetadataResponse class]; + query.loggingName = @"sheets.spreadsheets.developerMetadata.search"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsGet + +@dynamic includeGridData, ranges, spreadsheetId; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"ranges" : [NSString class] + }; + return map; +} + ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId { + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}"; + GTLRSheetsQuery_SpreadsheetsGet *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:nil + pathParameterNames:pathParams]; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_Spreadsheet class]; + query.loggingName = @"sheets.spreadsheets.get"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsGetByDataFilter + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_GetSpreadsheetByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}:getByDataFilter"; + GTLRSheetsQuery_SpreadsheetsGetByDataFilter *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_Spreadsheet class]; + query.loggingName = @"sheets.spreadsheets.getByDataFilter"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsSheetsCopyTo + +@dynamic sheetId, spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_CopySheetToAnotherSpreadsheetRequest *)object + spreadsheetId:(NSString *)spreadsheetId + sheetId:(NSInteger)sheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ + @"sheetId", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo"; + GTLRSheetsQuery_SpreadsheetsSheetsCopyTo *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.sheetId = sheetId; + query.expectedObjectClass = [GTLRSheets_SheetProperties class]; + query.loggingName = @"sheets.spreadsheets.sheets.copyTo"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesAppend + +@dynamic includeValuesInResponse, insertDataOption, range, + responseDateTimeRenderOption, responseValueRenderOption, spreadsheetId, + valueInputOption; + ++ (instancetype)queryWithObject:(GTLRSheets_ValueRange *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ + @"range", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values/{range}:append"; + GTLRSheetsQuery_SpreadsheetsValuesAppend *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.range = range; + query.expectedObjectClass = [GTLRSheets_AppendValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.append"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchClear + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchClearValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchClear"; + GTLRSheetsQuery_SpreadsheetsValuesBatchClear *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchClearValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchClear"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchClearByDataFilter + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchClearValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchClearByDataFilter"; + GTLRSheetsQuery_SpreadsheetsValuesBatchClearByDataFilter *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchClearValuesByDataFilterResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchClearByDataFilter"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchGet + +@dynamic dateTimeRenderOption, majorDimension, ranges, spreadsheetId, + valueRenderOption; + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + NSDictionary<NSString *, Class> *map = @{ + @"ranges" : [NSString class] + }; + return map; +} + ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId { + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchGet"; + GTLRSheetsQuery_SpreadsheetsValuesBatchGet *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:nil + pathParameterNames:pathParams]; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchGetValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchGet"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchGetByDataFilter + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchGetValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchGetByDataFilter"; + GTLRSheetsQuery_SpreadsheetsValuesBatchGetByDataFilter *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchGetValuesByDataFilterResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchGetByDataFilter"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchUpdate + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchUpdate"; + GTLRSheetsQuery_SpreadsheetsValuesBatchUpdate *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchUpdateValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchUpdate"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesBatchUpdateByDataFilter + +@dynamic spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_BatchUpdateValuesByDataFilterRequest *)object + spreadsheetId:(NSString *)spreadsheetId { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ @"spreadsheetId" ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values:batchUpdateByDataFilter"; + GTLRSheetsQuery_SpreadsheetsValuesBatchUpdateByDataFilter *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.expectedObjectClass = [GTLRSheets_BatchUpdateValuesByDataFilterResponse class]; + query.loggingName = @"sheets.spreadsheets.values.batchUpdateByDataFilter"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesClear + +@dynamic range, spreadsheetId; + ++ (instancetype)queryWithObject:(GTLRSheets_ClearValuesRequest *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ + @"range", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values/{range}:clear"; + GTLRSheetsQuery_SpreadsheetsValuesClear *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"POST" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.range = range; + query.expectedObjectClass = [GTLRSheets_ClearValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.clear"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesGet + +@dynamic dateTimeRenderOption, majorDimension, range, spreadsheetId, + valueRenderOption; + ++ (instancetype)queryWithSpreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range { + NSArray *pathParams = @[ + @"range", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values/{range}"; + GTLRSheetsQuery_SpreadsheetsValuesGet *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:nil + pathParameterNames:pathParams]; + query.spreadsheetId = spreadsheetId; + query.range = range; + query.expectedObjectClass = [GTLRSheets_ValueRange class]; + query.loggingName = @"sheets.spreadsheets.values.get"; + return query; +} + +@end + +@implementation GTLRSheetsQuery_SpreadsheetsValuesUpdate + +@dynamic includeValuesInResponse, range, responseDateTimeRenderOption, + responseValueRenderOption, spreadsheetId, valueInputOption; + ++ (instancetype)queryWithObject:(GTLRSheets_ValueRange *)object + spreadsheetId:(NSString *)spreadsheetId + range:(NSString *)range { + if (object == nil) { + GTLR_DEBUG_ASSERT(object != nil, @"Got a nil object"); + return nil; + } + NSArray *pathParams = @[ + @"range", @"spreadsheetId" + ]; + NSString *pathURITemplate = @"v4/spreadsheets/{spreadsheetId}/values/{range}"; + GTLRSheetsQuery_SpreadsheetsValuesUpdate *query = + [[self alloc] initWithPathURITemplate:pathURITemplate + HTTPMethod:@"PUT" + pathParameterNames:pathParams]; + query.bodyObject = object; + query.spreadsheetId = spreadsheetId; + query.range = range; + query.expectedObjectClass = [GTLRSheets_UpdateValuesResponse class]; + query.loggingName = @"sheets.spreadsheets.values.update"; + return query; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.h b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.h @@ -0,0 +1,103 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#if GTLR_BUILT_AS_FRAMEWORK + #import "GTLR/GTLRService.h" +#else + #import "GTLRService.h" +#endif + +#if GTLR_RUNTIME_VERSION != 3000 +#error This file was generated by a different version of ServiceGenerator which is incompatible with this GTLR library source. +#endif + +// Generated comments include content from the discovery document; avoid them +// causing warnings since clang's checks are some what arbitrary. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +NS_ASSUME_NONNULL_BEGIN + +// ---------------------------------------------------------------------------- +// Authorization scopes + +/** + * Authorization scope: See, edit, create, and delete all of your Google Drive + * files + * + * Value "https://www.googleapis.com/auth/drive" + */ +GTLR_EXTERN NSString * const kGTLRAuthScopeSheetsDrive; +/** + * Authorization scope: View and manage Google Drive files and folders that you + * have opened or created with this app + * + * Value "https://www.googleapis.com/auth/drive.file" + */ +GTLR_EXTERN NSString * const kGTLRAuthScopeSheetsDriveFile; +/** + * Authorization scope: See and download all your Google Drive files + * + * Value "https://www.googleapis.com/auth/drive.readonly" + */ +GTLR_EXTERN NSString * const kGTLRAuthScopeSheetsDriveReadonly; +/** + * Authorization scope: See, edit, create, and delete your spreadsheets in + * Google Drive + * + * Value "https://www.googleapis.com/auth/spreadsheets" + */ +GTLR_EXTERN NSString * const kGTLRAuthScopeSheetsSpreadsheets; +/** + * Authorization scope: View your Google Spreadsheets + * + * Value "https://www.googleapis.com/auth/spreadsheets.readonly" + */ +GTLR_EXTERN NSString * const kGTLRAuthScopeSheetsSpreadsheetsReadonly; + +// ---------------------------------------------------------------------------- +// GTLRSheetsService +// + +/** + * Service for executing Google Sheets API queries. + * + * Reads and writes Google Sheets. + */ +@interface GTLRSheetsService : GTLRService + +// No new methods + +// Clients should create a standard query with any of the class methods in +// GTLRSheetsQuery.h. The query can the be sent with GTLRService's execute +// methods, +// +// - (GTLRServiceTicket *)executeQuery:(GTLRQuery *)query +// completionHandler:(void (^)(GTLRServiceTicket *ticket, +// id object, NSError *error))handler; +// or +// - (GTLRServiceTicket *)executeQuery:(GTLRQuery *)query +// delegate:(id)delegate +// didFinishSelector:(SEL)finishedSelector; +// +// where finishedSelector has a signature of: +// +// - (void)serviceTicket:(GTLRServiceTicket *)ticket +// finishedWithObject:(id)object +// error:(NSError *)error; +// +// The object passed to the completion handler or delegate method +// is a subclass of GTLRObject, determined by the query method executed. + +@end + +NS_ASSUME_NONNULL_END + +#pragma clang diagnostic pop diff --git a/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.m b/Pods/GoogleAPIClientForREST/Source/GeneratedServices/Sheets/GTLRSheetsService.m @@ -0,0 +1,39 @@ +// NOTE: This file was generated by the ServiceGenerator. + +// ---------------------------------------------------------------------------- +// API: +// Google Sheets API (sheets/v4) +// Description: +// Reads and writes Google Sheets. +// Documentation: +// https://developers.google.com/sheets/ + +#import "GTLRSheets.h" + +// ---------------------------------------------------------------------------- +// Authorization scopes + +NSString * const kGTLRAuthScopeSheetsDrive = @"https://www.googleapis.com/auth/drive"; +NSString * const kGTLRAuthScopeSheetsDriveFile = @"https://www.googleapis.com/auth/drive.file"; +NSString * const kGTLRAuthScopeSheetsDriveReadonly = @"https://www.googleapis.com/auth/drive.readonly"; +NSString * const kGTLRAuthScopeSheetsSpreadsheets = @"https://www.googleapis.com/auth/spreadsheets"; +NSString * const kGTLRAuthScopeSheetsSpreadsheetsReadonly = @"https://www.googleapis.com/auth/spreadsheets.readonly"; + +// ---------------------------------------------------------------------------- +// GTLRSheetsService +// + +@implementation GTLRSheetsService + +- (instancetype)init { + self = [super init]; + if (self) { + // From discovery. + self.rootURLString = @"https://sheets.googleapis.com/"; + self.batchPath = @"batch"; + self.prettyPrintQueryParameterNames = @[ @"prettyPrint" ]; + } + return self; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011 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. + */ + +// Batch query documentation: +// https://github.com/google/google-api-objectivec-client-for-rest/wiki#batch-operations + +#import "GTLRQuery.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTLRBatchQuery : NSObject <GTLRQueryProtocol> + +/** + * Queries included in this batch. Each query should have a unique @c requestID. + */ +@property(atomic, copy, nullable) NSArray<GTLRQuery *> *queries; + +/** + * Flag indicating if query execution should skip authorization. Defaults to NO. + */ +@property(atomic, assign) BOOL shouldSkipAuthorization; + +/** + * Any additional HTTP headers for this batch. + * + * These headers override the same keys from the service object's + * @c additionalHTTPHeaders. + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalHTTPHeaders; + +/** + * Any additional URL query parameters to add to the batch query. + * + * These query parameters override the same keys from the service object's + * @c additionalURLQueryParameters + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalURLQueryParameters; + +/** + * The batch request multipart boundary, once determined. + */ +@property(atomic, copy, nullable) NSString *boundary; + +/** + * The brief string to identify this query in @c GTMSessionFetcher http logs. + * + * The default logging name for batch requests includes the API method names. + */ +@property(atomic, copy, nullable) NSString *loggingName; + +/** + * Constructor for a batch query, for use with @c addQuery: + */ ++ (instancetype)batchQuery; + +/** + * Constructor for a batch query, from an array of @c GTLRQuery objects. + */ ++ (instancetype)batchQueryWithQueries:(NSArray<GTLRQuery *> *)array; + +/** + * Add a single @c GTLRQuery to the batch. + */ +- (void)addQuery:(GTLRQuery *)query; + +/** + * Search the batch for a query with the specified ID. + */ +- (nullable GTLRQuery *)queryForRequestID:(NSString *)requestID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchQuery.m @@ -0,0 +1,179 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRBatchQuery.h" + +#import "GTLRService.h" + +#if DEBUG +static void DebugAssertValidBatchQueryItem(GTLRQuery *query) { + GTLR_DEBUG_ASSERT([query isKindOfClass:[GTLRQuery class]], + @"unexpected query class: %@", [query class]); + GTLR_DEBUG_ASSERT(query.uploadParameters == nil, + @"batch may not contain upload: %@", query); + GTLR_DEBUG_ASSERT(!query.hasExecutionParameters, + @"queries added to a batch may not contain executionParameters: %@", query); + GTLR_DEBUG_ASSERT(!query.queryInvalid, + @"batch may not contain query already executed: %@", query); +} +#else +static void DebugAssertValidBatchQueryItem(GTLRQuery *query) { } +#endif + +@implementation GTLRBatchQuery { + NSMutableArray<GTLRQuery *> *_queries; + NSMutableDictionary *_requestIDMap; + GTLRServiceExecutionParameters *_executionParameters; +} + +@synthesize shouldSkipAuthorization = _shouldSkipAuthorization, + additionalHTTPHeaders = _additionalHTTPHeaders, + additionalURLQueryParameters = _additionalURLQueryParameters, + boundary = _boundary, + loggingName = _loggingName; + ++ (instancetype)batchQuery { + GTLRBatchQuery *obj = [[self alloc] init]; + return obj; +} + ++ (instancetype)batchQueryWithQueries:(NSArray<GTLRQuery *> *)queries { + GTLRBatchQuery *obj = [self batchQuery]; + obj.queries = queries; + +#if DEBUG + for (GTLRQuery *query in queries) { + DebugAssertValidBatchQueryItem(query); + } +#endif + return obj; +} + +- (id)copyWithZone:(NSZone *)zone { + // Deep copy the list of queries + GTLRBatchQuery *newBatch = [[[self class] allocWithZone:zone] init]; + if (_queries) { + newBatch.queries = [[NSArray alloc] initWithArray:_queries + copyItems:YES]; + } + + // Using the executionParameters ivar avoids creating the object. + newBatch.executionParameters = _executionParameters; + + // Copied in the same order as synthesized above. + newBatch.shouldSkipAuthorization = _shouldSkipAuthorization; + newBatch.additionalHTTPHeaders = _additionalHTTPHeaders; + newBatch.additionalURLQueryParameters = _additionalURLQueryParameters; + newBatch.boundary = _boundary; + newBatch.loggingName = _loggingName; + + // No need to copy _requestIDMap as it's created on demand. + return newBatch; +} + +- (NSString *)description { + NSArray *queries = self.queries; + NSArray *loggingNames = [queries valueForKey:@"loggingName"]; + NSMutableSet *dedupedNames = [NSMutableSet setWithArray:loggingNames]; // de-dupe + [dedupedNames removeObject:[NSNull null]]; // In case any didn't have a loggingName. + NSString *namesStr = [[dedupedNames allObjects] componentsJoinedByString:@","]; + + return [NSString stringWithFormat:@"%@ %p (queries:%tu - %@)", + [self class], self, queries.count, namesStr]; +} + +#pragma mark - + +- (BOOL)isBatchQuery { + return YES; +} + +- (GTLRUploadParameters *)uploadParameters { + // File upload is not supported for batches + return nil; +} + +- (void)invalidateQuery { + NSArray *queries = self.queries; + [queries makeObjectsPerformSelector:@selector(invalidateQuery)]; + + _executionParameters = nil; +} + +- (GTLRQuery *)queryForRequestID:(NSString *)requestID { + GTLRQuery *result = [_requestIDMap objectForKey:requestID]; + if (result) return result; + + // We've not before tried to look up a query, or the map is stale + _requestIDMap = [[NSMutableDictionary alloc] init]; + + for (GTLRQuery *query in _queries) { + [_requestIDMap setObject:query forKey:query.requestID]; + } + + result = [_requestIDMap objectForKey:requestID]; + return result; +} + +#pragma mark - + +- (void)setQueries:(NSArray<GTLRQuery *> *)array { +#if DEBUG + for (GTLRQuery *query in array) { + DebugAssertValidBatchQueryItem(query); + } +#endif + + _queries = [array mutableCopy]; +} + +- (NSArray<GTLRQuery *> *)queries { + return _queries; +} + +- (void)addQuery:(GTLRQuery *)query { + DebugAssertValidBatchQueryItem(query); + + if (_queries == nil) { + _queries = [[NSMutableArray alloc] init]; + } + + [_queries addObject:query]; +} + +- (GTLRServiceExecutionParameters *)executionParameters { + @synchronized(self) { + if (!_executionParameters) { + _executionParameters = [[GTLRServiceExecutionParameters alloc] init]; + } + } + return _executionParameters; +} + +- (void)setExecutionParameters:(GTLRServiceExecutionParameters *)executionParameters { + @synchronized(self) { + _executionParameters = executionParameters; + } +} + +- (BOOL)hasExecutionParameters { + return _executionParameters.hasParameters; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2011 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 "GTLRObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GTLRErrorObject; + +/** + * A batch result includes a dictionary of successes, a dictionary of failures, and a dictionary of + * HTTP response headers. + * + * Dictionary keys are request ID strings; dictionary values are @c GTLRObject for + * successes, @c GTLRErrorObject for failures, @c NSDictionary for responseHeaders. + * + * For successes with no returned object (such as from delete operations), + * the object for the dictionary entry is @c NSNull. + * + * The original query for each result is available from the service ticket, as shown in + * the code snippet here. + * + * When the queries in the batch are unrelated, adding a @c completionBlock to each of + * the queries may be a simpler way to handle the batch results. + * + * @code + * NSDictionary *successes = batchResults.successes; + * for (NSString *requestID in successes) { + * GTLRObject *obj = successes[requestID]; + * GTLRQuery *query = [ticket queryForRequestID:requestID]; + * NSLog(@"Query %@ returned object %@", query, obj); + * } + * + * NSDictionary *failures = batchResults.failures; + * for (NSString *requestID in failures) { + * GTLRErrorObject *errorObj = failures[requestID]; + * GTLRQuery *query = [ticket queryForRequestID:requestID]; + * NSLog(@"Query %@ failed with error %@", query, errorObj); + * } + * @endcode + */ +@interface GTLRBatchResult : GTLRObject + +/** + * Object results of successful queries in the batch, keyed by request ID. + * + * Queries which do not return an object when successful have a @c NSNull value. + */ +@property(atomic, strong, nullable) NSDictionary<NSString *, __kindof GTLRObject *> *successes; + +/** + * Object results of unsuccessful queries in the batch, keyed by request ID. + */ +@property(atomic, strong, nullable) NSDictionary<NSString *, GTLRErrorObject *> *failures; + +/** + * Any HTTP response headers that were returned for a query request. Headers are optional therefore + * not all queries will have them. Query request with response headers are stored in a + * dictionary and keyed by request ID. + */ +@property(atomic, strong, nullable) + NSDictionary<NSString *, NSDictionary *> *responseHeaders; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRBatchResult.m @@ -0,0 +1,168 @@ +/* Copyright (c) 2011 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. +*/ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRBatchResult.h" + +#import "GTLRErrorObject.h" +#import "GTLRUtilities.h" + +static NSString *const kGTLRBatchResultSuccessesKeys = @"successesKeys"; +static NSString *const kGTLRBatchResultSuccessKeyPrefix = @"Success-"; +static NSString *const kGTLRBatchResultFailuresKeys = @"failuresKeys"; +static NSString *const kGTLRBatchResultFailurKeyPrefix = @"Failure-"; +static NSString *const kGTLRBatchResultResponseHeaders = @"responseHeaders"; + +@implementation GTLRBatchResult + +@synthesize successes = _successes, + failures = _failures, + responseHeaders = _responseHeaders; + +// Since this class doesn't use the json property, provide the basic NSObject +// methods needed to ensure proper behaviors. + +- (id)copyWithZone:(NSZone *)zone { + GTLRBatchResult* newObject = [super copyWithZone:zone]; + newObject.successes = [self.successes copyWithZone:zone]; + newObject.failures = [self.failures copyWithZone:zone]; + newObject.responseHeaders = [self.responseHeaders copyWithZone:zone]; + return newObject; +} + +- (NSUInteger)hash { + NSUInteger result = [super hash]; + result += result * 13 + [self.successes hash]; + result += result * 13 + [self.failures hash]; + result += result * 13 + [self.responseHeaders hash]; + return result; +} + +- (BOOL)isEqual:(id)object { + if (self == object) return YES; + + if (![super isEqual:object]) { + return NO; + } + + if (![object isKindOfClass:[GTLRBatchResult class]]) { + return NO; + } + + GTLRBatchResult *other = (GTLRBatchResult *)object; + if (!GTLR_AreEqualOrBothNil(self.successes, other.successes)) { + return NO; + } + if (!GTLR_AreEqualOrBothNil(self.failures, other.failures)) { + return NO; + } + return GTLR_AreEqualOrBothNil(self.responseHeaders, other.responseHeaders); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p (successes:%tu failures:%tu responseHeaders:%tu)", + [self class], self, + self.successes.count, self.failures.count, self.responseHeaders.count]; +} + +// This class is a subclass of GTLRObject, which declares NSSecureCoding +// conformance. Since this class does't really use the json property, provide +// a custom implementation to maintain the contract. +// +// For success/failures, one could do: +// [encoder encodeObject:self.successes forKey:kGTLRBatchResultSuccesses]; +// [encoder encodeObject:self.failures forKey:kGTLRBatchResultFailuresKeys]; +// and then use -decodeObjectOfClasses:forKey:, but nothing actually checks the +// structure of the dictionary, so instead the dicts are blown out to provide +// better validation by the encoder/decoder. + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (self) { + NSArray<NSString *> *keys = + [decoder decodeObjectOfClass:[NSArray class] + forKey:kGTLRBatchResultSuccessesKeys]; + if (keys.count) { + NSMutableDictionary *dict = + [NSMutableDictionary dictionaryWithCapacity:keys.count]; + for (NSString *key in keys) { + NSString *storageKey = + [kGTLRBatchResultSuccessKeyPrefix stringByAppendingString:key]; + GTLRObject *obj = [decoder decodeObjectOfClass:[GTLRObject class] + forKey:storageKey]; + if (obj) { + [dict setObject:obj forKey:key]; + } + } + self.successes = dict; + } + + keys = [decoder decodeObjectOfClass:[NSArray class] + forKey:kGTLRBatchResultFailuresKeys]; + if (keys.count) { + NSMutableDictionary *dict = + [NSMutableDictionary dictionaryWithCapacity:keys.count]; + for (NSString *key in keys) { + NSString *storageKey = + [kGTLRBatchResultFailurKeyPrefix stringByAppendingString:key]; + GTLRObject *obj = [decoder decodeObjectOfClass:[GTLRObject class] + forKey:storageKey]; + if (obj) { + [dict setObject:obj forKey:key]; + } + } + self.failures = dict; + } + + self.responseHeaders = + [decoder decodeObjectOfClass:[NSDictionary class] + forKey:kGTLRBatchResultResponseHeaders]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + [super encodeWithCoder:encoder]; + [encoder encodeObject:self.successes.allKeys + forKey:kGTLRBatchResultSuccessesKeys]; + [self.successes enumerateKeysAndObjectsUsingBlock:^(NSString *key, + GTLRObject * obj, + BOOL * stop) { + NSString *storageKey = + [kGTLRBatchResultSuccessKeyPrefix stringByAppendingString:key]; + [encoder encodeObject:obj forKey:storageKey]; + }]; + + [encoder encodeObject:self.failures.allKeys forKey:kGTLRBatchResultFailuresKeys]; + [self.failures enumerateKeysAndObjectsUsingBlock:^(NSString *key, + GTLRObject * obj, + BOOL * stop) { + NSString *storageKey = + [kGTLRBatchResultFailurKeyPrefix stringByAppendingString:key]; + [encoder encodeObject:obj forKey:storageKey]; + }]; + + [encoder encodeObject:self.responseHeaders + forKey:kGTLRBatchResultResponseHeaders]; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2011 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 <Foundation/Foundation.h> +#import "GTLRDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * An immutable class representing a date and optionally a time in UTC. + */ +@interface GTLRDateTime : NSObject <NSCopying> + +/** + * Constructor from a string representation. + */ ++ (nullable instancetype)dateTimeWithRFC3339String:(nullable NSString *)str; + +/** + * Constructor from a date and time representation. + */ ++ (instancetype)dateTimeWithDate:(NSDate *)date; + +/** + * Constructor from a date and time representation, along with an offset + * minutes value used when creating a RFC3339 string representation. + * + * The date value is independent of time zone; the offset affects how the + * date will be rendered as a string. + * + * The offsetMinutes may be initialized from a NSTimeZone as + * (timeZone.secondsFromGMT / 60) + */ ++ (instancetype)dateTimeWithDate:(NSDate *)date + offsetMinutes:(NSInteger)offsetMinutes; + +/** + * Constructor from a date for an all-day event. + * + * Use this constructor to create a @c GTLRDateTime that is "date only". + * + * @note @c hasTime will be set to NO. + */ ++ (instancetype)dateTimeForAllDayWithDate:(NSDate *)date; + +/** + * Constructor from date components. + */ ++ (instancetype)dateTimeWithDateComponents:(NSDateComponents *)date; + +/** + * The represented date and time. + * + * If @c hasTime is NO, the time is set to noon GMT so the date is valid for all time zones. + */ +@property(nonatomic, readonly) NSDate *date; + +/** + * The date and time as a RFC3339 string representation. + */ +@property(nonatomic, readonly) NSString *RFC3339String; + +/** + * The date and time as a RFC3339 string representation. + * + * This returns the same string as @c RFC3339String. + */ +@property(nonatomic, readonly) NSString *stringValue; + +/** + * The represented date and time as date components. + */ +@property(nonatomic, readonly, copy) NSDateComponents *dateComponents; + +/** + * The fraction of seconds represented, 0-999. + */ +@property(nonatomic, readonly) NSInteger milliseconds; + +/** + * The time offset displayed in the string representation, if any. + * + * If the offset is not nil, the date and time will be rendered as a string + * for the time zone indicated by the offset. + * + * An app may create a NSTimeZone for this with + * [NSTimeZone timeZoneForSecondsFromGMT:(offsetMinutes.integerValue * 60)] + */ +@property(nonatomic, readonly, nullable) NSNumber *offsetMinutes; + +/** + * Flag indicating if the object represents date only, or date with time. + */ +@property(nonatomic, readonly) BOOL hasTime; + +/** + * The calendar used by this class, Gregorian and UTC. + */ ++ (NSCalendar *)calendar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDateTime.m @@ -0,0 +1,373 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRDateTime.h" + +static NSUInteger const kGTLRDateComponentBits = (NSCalendarUnitYear | NSCalendarUnitMonth + | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute + | NSCalendarUnitSecond); + +@interface GTLRDateTime () + +- (void)setFromDate:(NSDate *)date; +- (void)setFromRFC3339String:(NSString *)str; + +@property(nonatomic, copy, readwrite) NSDateComponents *dateComponents; +@property(nonatomic, assign, readwrite) NSInteger milliseconds; +@property(nonatomic, strong, readwrite, nullable) NSNumber *offsetMinutes; + +@property(nonatomic, assign, readwrite) BOOL hasTime; + +@end + + +@implementation GTLRDateTime { + NSDate *_cachedDate; + NSString *_cachedRFC3339String; +} + +// A note about _milliseconds: +// RFC 3339 has support for fractions of a second. NSDateComponents is all +// NSInteger based, so it can't handle a fraction of a second. NSDate is +// built on NSTimeInterval so it has sub-millisecond precision. GTLR takes +// the compromise of supporting the RFC's optional fractional second support +// by maintaining a number of milliseconds past what fits in the +// NSDateComponents. The parsing and string conversions will include +// 3 decimal digits (hence milliseconds). When going to a string, the decimal +// digits are only included if the milliseconds are non zero. + +@dynamic date; +@dynamic RFC3339String; +@dynamic stringValue; +@dynamic hasTime; + +@synthesize dateComponents = _dateComponents, + milliseconds = _milliseconds, + offsetMinutes = _offsetMinutes; + ++ (instancetype)dateTimeWithRFC3339String:(NSString *)str { + if (str == nil) return nil; + + GTLRDateTime *result = [[self alloc] init]; + [result setFromRFC3339String:str]; + return result; +} + ++ (instancetype)dateTimeWithDate:(NSDate *)date { + if (date == nil) return nil; + + GTLRDateTime *result = [[self alloc] init]; + [result setFromDate:date]; + return result; +} + ++ (instancetype)dateTimeWithDate:(NSDate *)date + offsetMinutes:(NSInteger)offsetMinutes { + GTLRDateTime *result = [self dateTimeWithDate:date]; + result.offsetMinutes = @(offsetMinutes); + return result; +} + ++ (instancetype)dateTimeForAllDayWithDate:(NSDate *)date { + if (date == nil) return nil; + + GTLRDateTime *result = [[self alloc] init]; + [result setFromDate:date]; + result.hasTime = NO; + return result; +} + ++ (instancetype)dateTimeWithDateComponents:(NSDateComponents *)components { + NSCalendar *cal = components.calendar ?: [self calendar]; + NSDate *date = [cal dateFromComponents:components]; + + return [self dateTimeWithDate:date]; +} + +- (id)copyWithZone:(NSZone *)zone { + // Object is immutable + return self; +} + +- (BOOL)isEqual:(GTLRDateTime *)other { + if (self == other) return YES; + if (![other isKindOfClass:[GTLRDateTime class]]) return NO; + + BOOL areDateComponentsEqual = [self.dateComponents isEqual:other.dateComponents]; + if (!areDateComponentsEqual) return NO; + + NSNumber *offsetMinutes = self.offsetMinutes; + NSNumber *otherOffsetMinutes = other.offsetMinutes; + if ((offsetMinutes == nil) != (otherOffsetMinutes == nil) + || (offsetMinutes.integerValue != otherOffsetMinutes.integerValue)) return NO; + + return (self.milliseconds == other.milliseconds); +} + +- (NSUInteger)hash { + return [[self date] hash]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, self.RFC3339String]; +} + +- (NSDate *)date { + @synchronized(self) { + if (_cachedDate) return _cachedDate; + } + + NSDateComponents *dateComponents = self.dateComponents; + NSTimeInterval extraMillisecondsAsSeconds = 0.0; + NSCalendar *cal = [[self class] calendar]; + + if (!self.hasTime) { + // We're not keeping track of a time, but NSDate always is based on + // an absolute time. We want to avoid returning an NSDate where the + // calendar date appears different from what was used to create our + // date-time object. + // + // We'll make a copy of the date components, setting the time on our + // copy to noon GMT, since that ensures the date renders correctly for + // any time zone. + NSDateComponents *noonDateComponents = [dateComponents copy]; + [noonDateComponents setHour:12]; + [noonDateComponents setMinute:0]; + [noonDateComponents setSecond:0]; + dateComponents = noonDateComponents; + } else { + // Add in the fractional seconds that don't fit into NSDateComponents. + extraMillisecondsAsSeconds = ((NSTimeInterval)self.milliseconds) / 1000.0; + } + + NSDate *date = [cal dateFromComponents:dateComponents]; + + // Add in any milliseconds that didn't fit into the dateComponents. + if (extraMillisecondsAsSeconds > 0.0) { + date = [date dateByAddingTimeInterval:extraMillisecondsAsSeconds]; + } + + @synchronized(self) { + _cachedDate = date; + } + return date; +} + +- (NSString *)stringValue { + return self.RFC3339String; +} + +- (NSString *)RFC3339String { + @synchronized(self) { + if (_cachedRFC3339String) return _cachedRFC3339String; + } + + NSDateComponents *dateComponents = self.dateComponents; + + NSString *timeString = @""; // timeString like "T15:10:46-08:00" + + if (self.hasTime) { + NSString *fractionalSecondsString = @""; + if (self.milliseconds > 0.0) { + fractionalSecondsString = [NSString stringWithFormat:@".%03ld", (long)self.milliseconds]; + } + + // If the dateTime was created from a string with a time offset, render that back in + // and adjust the time. + NSString *offsetStr = @"Z"; + NSNumber *offsetMinutes = self.offsetMinutes; + if (offsetMinutes != nil) { + BOOL isNegative = NO; + NSInteger offsetVal = offsetMinutes.integerValue; + if (offsetVal < 0) { + isNegative = YES; + offsetVal = -offsetVal; + } + NSInteger mins = offsetVal % 60; + NSInteger hours = (offsetVal - mins) / 60; + offsetStr = [NSString stringWithFormat:@"%c%02ld:%02ld", + isNegative ? '-' : '+', (long)hours, (long)mins]; + + // Adjust date components back to account for the offset. + // + // This is the inverse of the adjustment done in setFromRFC3339String:. + if (offsetVal != 0) { + NSDate *adjustedDate = + [self.date dateByAddingTimeInterval:(offsetMinutes.integerValue * 60)]; + NSCalendar *calendar = [[self class] calendar]; + dateComponents = [calendar components:kGTLRDateComponentBits + fromDate:adjustedDate]; + } + } + + timeString = [NSString stringWithFormat:@"T%02ld:%02ld:%02ld%@%@", + (long)dateComponents.hour, (long)dateComponents.minute, + (long)dateComponents.second, fractionalSecondsString, + offsetStr]; + } + + // full dateString like "2006-11-17T15:10:46-08:00" + NSString *dateString = [NSString stringWithFormat:@"%04ld-%02ld-%02ld%@", + (long)dateComponents.year, (long)dateComponents.month, + (long)dateComponents.day, timeString]; + + @synchronized(self) { + _cachedRFC3339String = dateString; + } + return dateString; +} + +- (void)setFromDate:(NSDate *)date { + NSCalendar *cal = [[self class] calendar]; + + NSDateComponents *components = [cal components:kGTLRDateComponentBits + fromDate:date]; + self.dateComponents = components; + + // Extract the fractional seconds. + NSTimeInterval asTimeInterval = [date timeIntervalSince1970]; + NSTimeInterval worker = asTimeInterval - trunc(asTimeInterval); + self.milliseconds = (NSInteger)round(worker * 1000.0); +} + +- (void)setFromRFC3339String:(NSString *)str { + static NSCharacterSet *gDashSet; + static NSCharacterSet *gTSet; + static NSCharacterSet *gColonSet; + static NSCharacterSet *gPlusMinusZSet; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gDashSet = [NSCharacterSet characterSetWithCharactersInString:@"-"]; + gTSet = [NSCharacterSet characterSetWithCharactersInString:@"Tt "]; + gColonSet = [NSCharacterSet characterSetWithCharactersInString:@":"]; + gPlusMinusZSet = [NSCharacterSet characterSetWithCharactersInString:@"+-zZ"]; + }); + + NSInteger year = NSDateComponentUndefined; + NSInteger month = NSDateComponentUndefined; + NSInteger day = NSDateComponentUndefined; + NSInteger hour = NSDateComponentUndefined; + NSInteger minute = NSDateComponentUndefined; + NSInteger sec = NSDateComponentUndefined; + NSInteger milliseconds = 0; + double secDouble = -1.0; + NSString* sign = nil; + NSInteger offsetHour = 0; + NSInteger offsetMinute = 0; + + if (str.length > 0) { + NSScanner* scanner = [NSScanner scannerWithString:str]; + // There should be no whitespace, so no skip characters. + [scanner setCharactersToBeSkipped:nil]; + + // for example, scan 2006-11-17T15:10:46-08:00 + // or 2006-11-17T15:10:46Z + if (// yyyy-mm-dd + [scanner scanInteger:&year] && + [scanner scanCharactersFromSet:gDashSet intoString:NULL] && + [scanner scanInteger:&month] && + [scanner scanCharactersFromSet:gDashSet intoString:NULL] && + [scanner scanInteger:&day] && + // Thh:mm:ss + [scanner scanCharactersFromSet:gTSet intoString:NULL] && + [scanner scanInteger:&hour] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanInteger:&minute] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanDouble:&secDouble]) { + + // At this point we got secDouble, pull it apart. + sec = (NSInteger)secDouble; + double worker = secDouble - ((double)sec); + milliseconds = (NSInteger)round(worker * 1000.0); + + // Finish parsing, now the offset info. + if (// Z or +hh:mm + [scanner scanCharactersFromSet:gPlusMinusZSet intoString:&sign] && + [scanner scanInteger:&offsetHour] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanInteger:&offsetMinute]) { + } + } + } + + NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; + [dateComponents setYear:year]; + [dateComponents setMonth:month]; + [dateComponents setDay:day]; + [dateComponents setHour:hour]; + [dateComponents setMinute:minute]; + [dateComponents setSecond:sec]; + + BOOL isMinusOffset = [sign isEqual:@"-"]; + if (isMinusOffset || [sign isEqual:@"+"]) { + NSInteger totalOffsetMinutes = ((offsetHour * 60) + offsetMinute) * (isMinusOffset ? -1 : 1); + self.offsetMinutes = @(totalOffsetMinutes); + + // Minus offset means Universal time is that many hours and minutes ahead. + // + // This is the inverse of the adjustment done above in RFC3339String. + NSTimeInterval deltaOffsetSeconds = -totalOffsetMinutes * 60; + NSCalendar *calendar = [[self class] calendar]; + NSDate *scannedDate = [calendar dateFromComponents:dateComponents]; + NSDate *offsetDate = [scannedDate dateByAddingTimeInterval:deltaOffsetSeconds]; + + dateComponents = [calendar components:kGTLRDateComponentBits + fromDate:offsetDate]; + } + + self.dateComponents = dateComponents; + self.milliseconds = milliseconds; +} + +- (BOOL)hasTime { + NSDateComponents *dateComponents = self.dateComponents; + + BOOL hasTime = ([dateComponents hour] != NSDateComponentUndefined + && [dateComponents minute] != NSDateComponentUndefined); + + return hasTime; +} + +- (void)setHasTime:(BOOL)shouldHaveTime { + // We'll set time values to zero or kUndefinedDateComponent as appropriate. + BOOL hadTime = self.hasTime; + + if (shouldHaveTime && !hadTime) { + [_dateComponents setHour:0]; + [_dateComponents setMinute:0]; + [_dateComponents setSecond:0]; + _milliseconds = 0; + } else if (hadTime && !shouldHaveTime) { + [_dateComponents setHour:NSDateComponentUndefined]; + [_dateComponents setMinute:NSDateComponentUndefined]; + [_dateComponents setSecond:NSDateComponentUndefined]; + _milliseconds = 0; + } +} + ++ (NSCalendar *)calendar { + NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + cal.timeZone = (NSTimeZone * _Nonnull)[NSTimeZone timeZoneWithName:@"Universal"]; + return cal; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.h @@ -0,0 +1,83 @@ +/* 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 <Foundation/Foundation.h> +#import "GTLRDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * An immutable class representing a string data type 'google-duration'. + * It is based off the protocol buffers definition: + * https://github.com/google/protobuf/blob/master/src/google/protobuf/duration.proto + */ +@interface GTLRDuration : NSObject <NSCopying> + +/** + * Signed seconds of the span of time. Must be from -315,576,000,000 + * to +315,576,000,000 inclusive. + **/ +@property(nonatomic, readonly) int64_t seconds; + +/** + * Signed fractions of a second at nanosecond resolution of the span + * of time. Durations less than one second are represented with a 0 + * `seconds` field and a positive or negative `nanos` field. For durations + * of one second or more, a non-zero value for the `nanos` field must be + * of the same sign as the `seconds` field. Must be from -999,999,999 + * to +999,999,999 inclusive. + **/ +@property(nonatomic, readonly) int32_t nanos; + +/** + * This duration expressed as a NSTimeInterval. + * + * @note: Not all second/nanos combinations can be represented in a + * NSTimeInterval, so this could be a lossy transform. + **/ +@property(nonatomic, readonly) NSTimeInterval timeInterval; + +/** + * Returns the string form used to send this data type in a JSON payload. + */ +@property(nonatomic, readonly) NSString *jsonString; + +/** + * Constructor for a new duration with the given seconds and nanoseconds. + * + * Will fail if seconds/nanos differ in sign or if nanos is more than one + * second. + **/ ++ (nullable instancetype)durationWithSeconds:(int64_t)seconds + nanos:(int32_t)nanos; + +/** + * Constructor for a new duration from the given string form. + * + * Will return nil if jsonString is invalid. + **/ ++ (nullable instancetype)durationWithJSONString:(nullable NSString *)jsonString; + +/** + * Constructor for a new duration from the NSTimeInterval. + * + * @note NSTimeInterval doesn't always express things as exactly as one might + * expect, so coverting from to integer seconds & nanos can reveal this. + **/ ++ (instancetype)durationWithTimeInterval:(NSTimeInterval)timeInterval; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRDuration.m @@ -0,0 +1,222 @@ +/* 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRDuration.h" + +static const int32_t kNanosPerMillisecond = 1000000; +static const int32_t kNanosPerMicrosecond = 1000; +static const int32_t kNanosPerSecond = 1000000000; + +static int32_t IntPow10(int x) { + int32_t result = 1; + for (int i = 0; i < x; ++i) { + result *= 10; + } + return result; +} + +@implementation GTLRDuration + +@dynamic timeInterval; + +@synthesize seconds = _seconds, + nanos = _nanos, + jsonString = _jsonString; + ++ (instancetype)durationWithSeconds:(int64_t)seconds nanos:(int32_t)nanos { + if (seconds < 0) { + if (nanos > 0) { + // secs was -, nanos was + + return nil; + } + } else if (seconds > 0) { + if (nanos < 0) { + // secs was +, nanos was - + return nil; + } + } + if ((nanos <= -kNanosPerSecond) || (nanos >= kNanosPerSecond)) { + // more than a seconds worth + return nil; + } + return [[self alloc] initWithSeconds:seconds nanos:nanos jsonString:NULL]; +} + ++ (instancetype)durationWithJSONString:(NSString *)jsonString { + // It has to end in "s", so it needs >1 character. + if (jsonString.length <= 1) { + return nil; + } + + static NSCharacterSet *gNumberSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gNumberSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"]; + }); + + NSScanner* scanner = [NSScanner scannerWithString:jsonString]; + // There should be no whitespace, so no skip characters. + [scanner setCharactersToBeSkipped:nil]; + + // Can start with a '-'. + BOOL isNeg = [scanner scanString:@"-" intoString:NULL]; + + int64_t seconds; + if (![scanner scanLongLong:&seconds]) { + return nil; + } + + // Since the sign was manually scanned, seconds should be positive + // (i.e. no "--#" in the put). + if (seconds < 0) { + return nil; + } + + // See if it has a ".[nanos]". Spec seems to say it is required, but play + // it safe and make it optional. + int32_t nanos = 0; + if ([scanner scanString:@"." intoString:NULL]) { + NSString *nanosStr; + if (![scanner scanCharactersFromSet:gNumberSet intoString:&nanosStr]) { + return nil; + } + // Ensure not too many digits (also ensure it is within range). + if (nanosStr.length > 9) { + return nil; + } + // Can use NSString's intValue since the character set was controlled. + nanos = [nanosStr intValue]; + // Scale based on length. + nanos *= IntPow10(9 - (int)nanosStr.length); + } + + // And must have the final 's'. + if (![scanner scanString:@"s" intoString:NULL]) { + return nil; + } + + // Better be the end... + if (![scanner isAtEnd]) { + return nil; + } + + if (isNeg) { + seconds = -seconds; + nanos = -nanos; + } + + // Pass on the json string so it will be reflected back out as it came in + // (incase it had a different number of digits, etc). + return [[self alloc] initWithSeconds:seconds + nanos:nanos + jsonString:jsonString]; +} + ++ (instancetype)durationWithTimeInterval:(NSTimeInterval)timeInterval { + NSTimeInterval seconds; + NSTimeInterval nanos = modf(timeInterval, &seconds); + nanos *= (NSTimeInterval)kNanosPerSecond; + + return [[self alloc] initWithSeconds:(int64_t)seconds + nanos:(int32_t)nanos + jsonString:NULL]; +} + +- (instancetype)init { + return [self initWithSeconds:0 nanos:0 jsonString:NULL]; +} + +- (instancetype)initWithSeconds:(int64_t)seconds + nanos:(int32_t)nanos + jsonString:(NSString *)jsonString { + self = [super init]; + if (self) { + // Sanity asserts, the class methods should make sure this doesn't happen. + GTLR_DEBUG_ASSERT((((seconds <= 0) && (nanos <= 0)) || + ((seconds >= 0) && (nanos >= 0))), + @"Seconds and nanos must have the same sign (%lld & %d)", + seconds, nanos); + GTLR_DEBUG_ASSERT(((nanos < kNanosPerSecond) && + (nanos > -kNanosPerSecond)), + @"Nanos is a second or more (%d)", nanos); + + _seconds = seconds; + _nanos = nanos; + + if (jsonString.length) { + _jsonString = [jsonString copy]; + } else { + // Based off the JSON serialization code in protocol buffers + // ( https://github.com/google/protobuf/ ). + NSString *sign = @""; + if ((seconds < 0) || (nanos < 0)) { + sign = @"-"; + seconds = -seconds; + nanos = -nanos; + } + int nanoDigts; + int32_t nanoDivider; + if (nanos % kNanosPerMillisecond == 0) { + nanoDigts = 3; + nanoDivider = kNanosPerMillisecond; + } else if (nanos % kNanosPerMicrosecond == 0) { + nanoDigts = 6; + nanoDivider = kNanosPerMicrosecond; + } else { + nanoDigts = 9; + nanoDivider = 1; + } + _jsonString = [NSString stringWithFormat:@"%@%lld.%0*ds", + sign, seconds, nanoDigts, (nanos / nanoDivider)]; + } + } + return self; +} + +- (NSTimeInterval)timeInterval { + NSTimeInterval result = self.seconds; + result += (NSTimeInterval)self.nanos / (NSTimeInterval)kNanosPerSecond; + return result; +} + +- (id)copyWithZone:(NSZone *)zone { + // Object is immutable + return self; +} + +- (BOOL)isEqual:(GTLRDuration *)other { + if (self == other) return YES; + if (![other isKindOfClass:[GTLRDuration class]]) return NO; + + BOOL result = ((self.seconds == other.seconds) && + (self.nanos == other.nanos)); + return result; +} + +- (NSUInteger)hash { + NSUInteger result = (NSUInteger)((self.seconds * 13) + self.nanos); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, self.jsonString]; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2011 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 "GTLRObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GTLRErrorObjectErrorItem; +@class GTLRErrorObjectDetail; + +/** + * This class wraps JSON responses (both V1 and V2 of Google JSON errors) and NSErrors. + * + * A GTLRErrorObject can be created using +objectWithJSON: or +objectWithFoundationError: + */ +@interface GTLRErrorObject : GTLRObject + +/** + * Convenience method for creating an error object from an NSError. + * + * @param error The @c NSError to be encapsulated by the @c GTLRErrorObject + * + * @return A @c GTLRErrorObject wrapping the NSError. + */ ++ (instancetype)objectWithFoundationError:(NSError *)error; + +/** + * Convenience utility for extracting the GTLRErrorObject that was used to create an NSError. + * + * @param foundationError The NSError that may have been obtained from a GTLRErrorObject. + * + * @return The GTLRErrorObject, nil if the error was not originally from a GTLRErrorObject. + */ ++ (nullable GTLRErrorObject *)underlyingObjectForError:(NSError *)foundationError; + +// +// V1 & V2 properties. +// + +/** + * The numeric error code. + */ +@property(nonatomic, strong, nullable) NSNumber *code; + +/** + * An error message string, typically provided by the API server. This is not localized, + * and its reliability depends on the API server. + */ +@property(nonatomic, strong, nullable) NSString *message; + +// +// V1 properties. +// + +/** + * Underlying errors that occurred on the server. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRErrorObjectErrorItem *> *errors; + +// +// V2 properties +// + +/** + * A status error string, defined by the API server, such as "NOT_FOUND". + */ +@property(nonatomic, strong, nullable) NSString *status; + +/** + * Additional diagnostic error details provided by the API server. + */ +@property(nonatomic, strong, nullable) NSArray<GTLRErrorObjectDetail *> *details; + +/** + * An NSError, either underlying the error object or manufactured from the error object's + * properties. + */ +@property(nonatomic, readonly) NSError *foundationError; + +@end + +/** + * Class representing the items of the "errors" array inside the Google V1 error JSON. + * + * Client applications should not rely on the property values of these items. + */ +@interface GTLRErrorObjectErrorItem : GTLRObject +@property(nonatomic, strong, nullable) NSString *domain; +@property(nonatomic, strong, nullable) NSString *reason; +@property(nonatomic, strong, nullable) NSString *message; +@property(nonatomic, strong, nullable) NSString *location; +@end + +/** + * Class representing the items of the "details" array inside the Google V2 error JSON. + * + * Client applications should not rely on the property values of these items. + */ +@interface GTLRErrorObjectDetail : GTLRObject +@property(nonatomic, strong, nullable) NSString *type; +@property(nonatomic, strong, nullable) NSString *detail; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRErrorObject.m @@ -0,0 +1,140 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRErrorObject.h" + +#import "GTLRUtilities.h" +#import "GTLRService.h" + +static NSString *const kGTLRErrorObjectFoundationErrorKey = @"foundationError"; + +@implementation GTLRErrorObject { + NSError *_originalFoundationError; +} + +// V1 & V2 properties. +@dynamic code; +@dynamic message; + +// V1 properties. +@dynamic errors; + +// V2 properties. +@dynamic status; +@dynamic details; + +// Implemented below. +@dynamic foundationError; + ++ (instancetype)objectWithFoundationError:(NSError *)error { + GTLRErrorObject *object = [self object]; + object->_originalFoundationError = error; + object.code = @(error.code); + object.message = error.localizedDescription; + return object; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + return @{ + @"errors" : [GTLRErrorObjectErrorItem class], + @"details" : [GTLRErrorObjectDetail class] + }; +} + +- (NSError *)foundationError { + // If there was an original foundation error, copy its userInfo as the basis for ours. + NSMutableDictionary *userInfo = + [NSMutableDictionary dictionaryWithDictionary:_originalFoundationError.userInfo]; + + // This structured GTLRErrorObject will be available in the error's userInfo + // dictionary. + userInfo[kGTLRStructuredErrorKey] = self; + + NSError *error; + if (_originalFoundationError) { + error = [NSError errorWithDomain:_originalFoundationError.domain + code:_originalFoundationError.code + userInfo:userInfo]; + } else { + NSString *reasonStr = self.message; + if (reasonStr) { + userInfo[NSLocalizedDescriptionKey] = reasonStr; + } + + error = [NSError errorWithDomain:kGTLRErrorObjectDomain + code:self.code.integerValue + userInfo:userInfo]; + } + return error; +} + ++ (GTLRErrorObject *)underlyingObjectForError:(NSError *)foundationError { + NSDictionary *userInfo = [foundationError userInfo]; + GTLRErrorObject *errorObj = [userInfo objectForKey:kGTLRStructuredErrorKey]; + return errorObj; +} + +- (BOOL)isEqual:(id)object { + // Include the underlying foundation error in equality checks. + if (self == object) return YES; + if (![super isEqual:object]) return NO; + if (![object isKindOfClass:[GTLRErrorObject class]]) return NO; + GTLRErrorObject *other = (GTLRErrorObject *)object; + return GTLR_AreEqualOrBothNil(_originalFoundationError, + other->_originalFoundationError); +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (self) { + _originalFoundationError = + [decoder decodeObjectOfClass:[NSError class] + forKey:kGTLRErrorObjectFoundationErrorKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + [super encodeWithCoder:encoder]; + [encoder encodeObject:_originalFoundationError + forKey:kGTLRErrorObjectFoundationErrorKey]; +} + +@end + +@implementation GTLRErrorObjectErrorItem +@dynamic domain; +@dynamic reason; +@dynamic message; +@dynamic location; +@end + +@implementation GTLRErrorObjectDetail +@dynamic type; +@dynamic detail; + ++ (NSDictionary *)propertyToJSONKeyMap { + return @{ @"type" : @"@type" }; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRObject.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRObject.h @@ -0,0 +1,317 @@ +/* Copyright (c) 2011 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. + */ + +// GTLRObject documentation: +// https://github.com/google/google-api-objectivec-client-for-rest/wiki#objects-and-queries + +#import <Foundation/Foundation.h> + +#import "GTLRDefines.h" +#import "GTLRDateTime.h" +#import "GTLRDuration.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GTLRObject; + +/** + * Protocol that can be implemented to provide custom logic for what class + * should be created out of the given JSON. + */ +@protocol GTLRObjectClassResolver <NSObject> +- (Class)classForJSON:(NSDictionary *)json + defaultClass:(Class)defaultClass; +@end + +/** + * Standard GTLRObjectClassResolver used by the core library. + */ +@interface GTLRObjectClassResolver : NSObject<GTLRObjectClassResolver> + +/** + * Returns a resolver that will look up the 'kind' properties to find classes + * based on the JSON. + * + * The generated service classes provide a +kindStringToClassMap method for any + * mappings that were found from discovery when generating the service. + */ ++ (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap; + +/** + * Returns a resolver that will look up the 'kind' properties to find classes + * based on the JSON and then applies mapping of surrogate classes to swap out + * specific classes. + * + * Surrogates are subclasses to be instantiated instead of standard classes + * when creating objects from the JSON. For example, this code will, for one query's + * execution, swap a service's default resolver for one that will then use + * MyCalendarEventSubclass instead of GTLRCalendarEvent and + * MyCalendarReminderSubclass instead of GTLRCalendarReminder. + * + * @code + * NSDictionary *surrogates = @{ + * [GTLRCalendarEvent class] : [MyCalendarEventSubclass class] + * [GTLRCalendarReminder class] : [MyCalendarReminderSubclass class], + * }; + * NSDictionary *serviceKindMap = [[calendarService class] kindStringToClassMap]; + * GTLRObjectClassResolver *updatedResolver = + * [GTLRObjectClassResolver resolverWithKindMap:serviceKindMap + * surrogates:surrogates]; + * query.executionParameters.objectClassResolver = updatedResolver; + * @endcode + * + * @note To install surrogates for all queries executed by the service, use + * the service's @c -setSurrogates method. + */ ++ (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap + surrogates:(NSDictionary<Class, Class> *)surrogates; + +@end + +/** + * @c GTLRObject serves as the common superclass for classes wrapping JSON, errors, and other data + * passed in server requests and responses. + * + * @note This class is @em not safe for simultaneous use from multiple threads. Applications should + * serialize or protect access to a @c GTLRObject instance as they would for any standard + * Cocoa mutable container. + */ +@interface GTLRObject : NSObject <NSCopying, NSSecureCoding> + +/** + * The JSON underlying the property values for this object. + * + * The JSON should be accessed or set using the generated properties of a + * class derived from GTLRObject or with the methods @c setJSONValue:forKey: + * and @c JSONValueForKey: + * + * @note: Applications should use @c additionalPropertyForKey: when accessing + * API object properties that do not have generated @c \@property accessors. + */ +@property(nonatomic, strong, nullable) NSMutableDictionary *JSON; + +/** + * A dictionary retained by the object for the convenience of the client application. + * + * A client application may use this to retain any dictionary. + * + * The values of the user properties dictionary will not be sent to the server during + * query execution, and will not be copied by NSCopying or encoded by NSSecureCoding. + */ +@property(nonatomic, strong) NSDictionary *userProperties; + +///////////////////////////////////////////////////////////////////////////////////////////// +// +// Public methods +// +// These methods are intended for users of the library +// +///////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor for an empty object. + */ ++ (instancetype)object; + +/** + * Constructor for an object including JSON. + */ ++ (instancetype)objectWithJSON:(nullable NSDictionary *)dict; + +/** + * Constructor for an object including JSON and providing a resolver to help + * select the correct classes for sub objects within the json. + * + * The generated services provide a default resolver (-objectClassResolver) + * that covers the kinds for that service. They also expose the kind mappings + * via the +kindStringToClassMap method. + */ ++ (instancetype)objectWithJSON:(nullable NSDictionary *)dict + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver; + +/** + * The JSON for the object, or an empty string if there is no JSON or if the JSON + * dictionary cannot be represented as JSON. + */ +- (NSString *)JSONString; + +/** + * Generic access for setting entries in the JSON dictionary. This creates the JSON dictionary + * if necessary. + * + * @note: Applications should use @c setAdditionalProperty:forKey: when setting + * API object properties that do not have generated @c \@property accessors. + */ +- (void)setJSONValue:(nullable id)obj forKey:(nonnull NSString *)key; + +/** + * Generic access to the JSON dictionary. + * + * @note: Applications should use @c additionalPropertyForKey: when accessing + * API object properties that do not have generated @c \@property accessors. + */ +- (nullable id)JSONValueForKey:(NSString *)key; + +/** + * The list of keys in this object's JSON that are not listed as properties on the object. + */ +- (nullable NSArray<NSString *> *)additionalJSONKeys; + +/** + * Setter for any key in the JSON that is not listed as a @c \@property in the class declaration. + */ +- (void)setAdditionalProperty:(id)obj forName:(NSString *)name; + +/** + * Accessor for any key in the JSON that is not listed as a @c \@property in the class + * declaration. + */ +- (nullable id)additionalPropertyForName:(NSString *)name; + +/** + * A dictionary of all keys in the JSON that is not listed as a @c \@property in the class + * declaration. + */ +- (NSDictionary<NSString *, id> *)additionalProperties; + +/** + * A string for a partial query describing the fields present. + * + * @note Only the first element of any array is examined. + * + * @see https://developers.google.com/google-apps/tasks/performance?csw=1#partial + * + * @return A @c fields string describing the fields present in the object. + */ +- (NSString *)fieldsDescription; + +/** + * An object containing only the changes needed to do a partial update (patch), + * where the patch would be to change an object from the original to the receiver, + * such as + * @c GTLRSomeObject *patchObject = [newVersion patchObjectFromOriginal:oldVersion]; + * + * @note This method returns nil if there are no changes between the original and the receiver. + * + * @see https://developers.google.com/google-apps/tasks/performance?csw=1#patch + * + * @param original The original object from which to create the patch object. + * + * @return The object used for the patch body. + */ +- (nullable id)patchObjectFromOriginal:(GTLRObject *)original; + +/** + * A null value to set object properties for patch queries that delete fields. + * + * Do not use this except when setting an object property for a patch query. + * + * @return The null value object. + */ ++ (id)nullValue; + +#pragma mark Internal + +/////////////////////////////////////////////////////////////////////////////// +// +// Protected methods +// +// These methods are intended for subclasses of GTLRObject +// + +// Creation of objects from a JSON dictionary. The class created depends on +// the content of the JSON, not the class messaged. ++ (nullable GTLRObject *)objectForJSON:(NSMutableDictionary *)json + defaultClass:(nullable Class)defaultClass + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver; + +// Property-to-key mapping (for JSON keys which are not used as method names) ++ (nullable NSDictionary<NSString *, NSString *> *)propertyToJSONKeyMap; + +// property-to-Class mapping for array properties (to say what is in the array) ++ (nullable NSDictionary<NSString *, Class> *)arrayPropertyToClassMap; + +// The default class for additional JSON keys ++ (nullable Class)classForAdditionalProperties; + +// Indicates if a "kind" property on this class can be used for the class +// registry or if it appears to be non standard. ++ (BOOL)isKindValidForClassRegistry; + +@end + +/** + * Collection results have a property containing an array of @c GTLRObject + * + * This provides support for @c NSFastEnumeration and for indexed subscripting to + * access the objects in the array. + */ +@interface GTLRCollectionObject : GTLRObject<NSFastEnumeration> + +/** + * The property name that holds the collection. + * + * @return The key for the property holding the array of @c GTLRObject items. + */ ++ (NSString *)collectionItemsKey; + +// objectAtIndexedSubscript: will throw if the index is out of bounds (like +// NSArray does). +- (nullable id)objectAtIndexedSubscript:(NSUInteger)idx; + +@end + +/** + * A GTLRDataObject holds media data and the MIME type of the data returned by a media + * download query. + * + * The JSON for the object may be nil. + */ +@interface GTLRDataObject : GTLRObject + +/** + * The downloaded media data. + */ +@property(atomic, strong) NSData *data; + +/** + * The MIME type of the downloaded media data. + */ +@property(atomic, copy) NSString *contentType; + +@end + +/** + * Base class used when a service method directly returns an array instead + * of a JSON object. This exists for the methods not up to spec. + */ +@interface GTLRResultArray : GTLRCollectionObject + +/** + * This method should only be called by subclasses. + */ +- (nullable NSArray *)itemsWithItemClass:(Class)itemClass; +@end + +/** + * Helper to call the resolver and find the class to use for the given JSON. + * Intended for internal library use only. + */ +Class GTLRObjectResolveClass( + id<GTLRObjectClassResolver> objectClassResolver, + NSDictionary *json, + Class defaultClass); + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRObject.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRObject.m @@ -0,0 +1,760 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#include <objc/runtime.h> + +#import "GTLRObject.h" +#import "GTLRRuntimeCommon.h" +#import "GTLRUtilities.h" + +static NSString *const kUserDataPropertyKey = @"_userData"; + +static NSString *const kGTLRObjectJSONCoderKey = @"json"; + +static NSMutableDictionary *DeepMutableCopyOfJSONDictionary(NSDictionary *initialJSON); + +@interface GTLRObject () <GTLRRuntimeCommon> + +@property(nonatomic, strong) id<GTLRObjectClassResolver>objectClassResolver; + +@end + +@implementation GTLRObject { + // Any complex object hung off this object goes into the cache so the + // next fetch will get the same object back instead of having to recreate + // it. + NSMutableDictionary *_childCache; +} + +@synthesize JSON = _json, + objectClassResolver = _objectClassResolver, + userProperties = _userProperties; + ++ (instancetype)object { + return [[self alloc] init]; +} + ++ (instancetype)objectWithJSON:(NSDictionary *)dict { + GTLRObject *obj = [self object]; + obj->_json = DeepMutableCopyOfJSONDictionary(dict); + return obj; +} + ++ (instancetype)objectWithJSON:(nullable NSDictionary *)dict + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver { + GTLRObject *obj = [self objectWithJSON:dict]; + obj->_objectClassResolver = objectClassResolver; + return obj; +} + ++ (NSDictionary<NSString *, NSString *> *)propertyToJSONKeyMap { + return nil; +} + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + return nil; +} + ++ (Class)classForAdditionalProperties { + return Nil; +} + ++ (BOOL)isKindValidForClassRegistry { + return YES; +} + +- (BOOL)isEqual:(GTLRObject *)other { + if (self == other) return YES; + if (other == nil) return NO; + + // The objects should be the same class, or one should be a subclass of the + // other's class + if (![other isKindOfClass:[self class]] + && ![self isKindOfClass:[other class]]) return NO; + + // What we're not comparing here: + // properties + return GTLR_AreEqualOrBothNil(_json, [other JSON]); +} + +// By definition, for two objects to potentially be considered equal, +// they must have the same hash value. The hash is mostly ignored, +// but removeObjectsInArray: in Leopard does seem to check the hash, +// and NSObject's default hash method just returns the instance pointer. +// We'll define hash here for all of our GTLRObjects. +- (NSUInteger)hash { + return (NSUInteger) (__bridge void *) [GTLRObject class]; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLRObject *newObject = [[[self class] allocWithZone:zone] init]; + newObject.JSON = DeepMutableCopyOfJSONDictionary(self.JSON); + newObject.objectClassResolver = self.objectClassResolver; + + // What we're not copying: + // userProperties + return newObject; +} + +- (NSString *)descriptionWithLocale:(id)locale { + return self.description; +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super init]; + if (self) { + _json = [decoder decodeObjectOfClass:[NSMutableDictionary class] + forKey:kGTLRObjectJSONCoderKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:_json forKey:kGTLRObjectJSONCoderKey]; +} + +#pragma mark JSON values + +- (void)setJSONValue:(id)obj forKey:(NSString *)key { + NSMutableDictionary *dict = self.JSON; + if (dict == nil && obj != nil) { + dict = [NSMutableDictionary dictionaryWithCapacity:1]; + self.JSON = dict; + } + [dict setValue:obj forKey:key]; +} + +- (id)JSONValueForKey:(NSString *)key { + id obj = [self.JSON objectForKey:key]; + return obj; +} + +- (NSString *)JSONString { + NSError *error; + NSDictionary *json = self.JSON; + if (json) { + NSData *data = [NSJSONSerialization dataWithJSONObject:json + options:NSJSONWritingPrettyPrinted + error:&error]; + GTLR_DEBUG_ASSERT(data != nil, @"JSONString generate failed: %@\n JSON: %@", error, json); + if (data) { + NSString *jsonStr = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + if (jsonStr) return jsonStr; + } + } + return @""; +} + +- (NSArray<NSString *> *)additionalJSONKeys { + NSArray *knownKeys = [[self class] allKnownKeys]; + NSMutableArray *result; + NSArray *allKeys = _json.allKeys; + if (allKeys) { + result = [NSMutableArray arrayWithArray:allKeys]; + [result removeObjectsInArray:knownKeys]; + // Return nil instead of an empty array. + if (result.count == 0) { + result = nil; + } + } + return result; +} + +#pragma mark Partial - Fields + +- (NSString *)fieldsDescription { + NSString *str = [GTLRObject fieldsDescriptionForJSON:self.JSON]; + return str; +} + ++ (NSString *)fieldsDescriptionForJSON:(NSDictionary *)targetJSON { + // Internal routine: recursively generate a string field description + // by joining elements + NSArray *array = [self fieldsElementsForJSON:targetJSON]; + NSString *str = [array componentsJoinedByString:@","]; + return str; +} + ++ (NSArray *)fieldsElementsForJSON:(NSDictionary *)targetJSON { + // Internal routine: recursively generate an array of field description + // element strings + NSMutableArray *resultFields = [NSMutableArray array]; + + // Sorting the dictionary keys gives us deterministic results when iterating + NSArray *sortedKeys = [targetJSON.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + for (NSString *key in sortedKeys) { + // We'll build a comma-separated list of fields + id value = [targetJSON objectForKey:key]; + if ([value isKindOfClass:[NSString class]] + || [value isKindOfClass:[NSNumber class]]) { + // Basic type (string, number), so the key is what we want + [resultFields addObject:key]; + } else if ([value isKindOfClass:[NSDictionary class]]) { + // Object (dictionary): "parent/child1,parent/child2,parent/child3" + NSArray *subElements = [self fieldsElementsForJSON:value]; + for (NSString *subElem in subElements) { + NSString *prepended = [NSString stringWithFormat:@"%@/%@", + key, subElem]; + [resultFields addObject:prepended]; + } + } else if ([value isKindOfClass:[NSArray class]]) { + // Array; we'll generate from the first array entry: + // "parent(child1,child2,child3)" + // + // Open question: should this instead create the union of elements for + // all items in the array, rather than just get fields from the first + // array object? + if (((NSArray *)value).count > 0) { + id firstObj = [value objectAtIndex:0]; + if ([firstObj isKindOfClass:[NSDictionary class]]) { + // An array of objects + NSString *contentsStr = [self fieldsDescriptionForJSON:firstObj]; + NSString *encapsulated = [NSString stringWithFormat:@"%@(%@)", + key, contentsStr]; + [resultFields addObject:encapsulated]; + } else { + // An array of some basic type, or of arrays + [resultFields addObject:key]; + } + } + } else { + GTLR_ASSERT(0, @"GTLRObject unknown field element for %@ (%@)", + key, NSStringFromClass([value class])); + } + } + return resultFields; +} + +#pragma mark Partial - Patch + +- (id)patchObjectFromOriginal:(GTLRObject *)original { + GTLRObject *resultObj; + NSMutableDictionary *resultJSON = [GTLRObject patchDictionaryForJSON:self.JSON + fromOriginalJSON:original.JSON]; + if (resultJSON.count > 0) { + // Avoid an extra copy by assigning the JSON directly rather than using +objectWithJSON: + resultObj = [[self class] object]; + resultObj.JSON = resultJSON; + } else { + // Client apps should not attempt to patch with an object containing + // empty JSON + resultObj = nil; + } + return resultObj; +} + ++ (NSMutableDictionary *)patchDictionaryForJSON:(NSDictionary *)newJSON + fromOriginalJSON:(NSDictionary *)originalJSON { + // Internal recursive routine to create an object suitable for + // our patch semantics + NSMutableDictionary *resultJSON = [NSMutableDictionary dictionary]; + + // Iterate through keys present in the old object + NSArray *originalKeys = originalJSON.allKeys; + for (NSString *key in originalKeys) { + id originalValue = [originalJSON objectForKey:key]; + id newValue = [newJSON valueForKey:key]; + if (newValue == nil) { + // There is no new value for this key, so set the value to NSNull + [resultJSON setValue:[NSNull null] forKey:key]; + } else if (!GTLR_AreEqualOrBothNil(originalValue, newValue)) { + // The values for this key differ + if ([originalValue isKindOfClass:[NSDictionary class]] + && [newValue isKindOfClass:[NSDictionary class]]) { + // Both are objects; recurse + NSMutableDictionary *subDict = [self patchDictionaryForJSON:newValue + fromOriginalJSON:originalValue]; + [resultJSON setValue:subDict forKey:key]; + } else { + // They are non-object values; the new replaces the old. Per the + // documentation for patch, this replaces entire arrays. + [resultJSON setValue:newValue forKey:key]; + } + } else { + // The values are the same; omit this key-value pair + } + } + + // Iterate through keys present only in the new object, and add them to the + // result + NSMutableArray *newKeys = [NSMutableArray arrayWithArray:newJSON.allKeys]; + [newKeys removeObjectsInArray:originalKeys]; + + for (NSString *key in newKeys) { + id value = [newJSON objectForKey:key]; + [resultJSON setValue:value forKey:key]; + } + return resultJSON; +} + ++ (id)nullValue { + return [NSNull null]; +} + +#pragma mark Additional Properties + +- (id)additionalPropertyForName:(NSString *)name { + // Return the cached object, if any, before creating one. + id result = [self cacheChildForKey:name]; + if (result != nil) { + return result; + } + + Class defaultClass = [[self class] classForAdditionalProperties]; + id jsonObj = [self JSONValueForKey:name]; + BOOL shouldCache = NO; + if (jsonObj != nil) { + id<GTLRObjectClassResolver>objectClassResolver = self.objectClassResolver; + result = [GTLRRuntimeCommon objectFromJSON:jsonObj + defaultClass:defaultClass + objectClassResolver:objectClassResolver + isCacheable:&shouldCache]; + } + + [self setCacheChild:(shouldCache ? result : nil) + forKey:name]; + return result; +} + +- (void)setAdditionalProperty:(id)obj forName:(NSString *)name { + BOOL shouldCache = NO; + Class defaultClass = [[self class] classForAdditionalProperties]; + id json = [GTLRRuntimeCommon jsonFromAPIObject:obj + expectedClass:defaultClass + isCacheable:&shouldCache]; + [self setJSONValue:json forKey:name]; + [self setCacheChild:(shouldCache ? obj : nil) + forKey:name]; +} + +- (NSDictionary<NSString *, id> *)additionalProperties { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + NSArray *propertyNames = [self additionalJSONKeys]; + for (NSString *name in propertyNames) { + id obj = [self additionalPropertyForName:name]; + [result setObject:obj forKey:name]; + } + + return result; +} + +#pragma mark Child Cache methods + +// There is no property for _childCache as there shouldn't be KVC/KVO +// support for it, it's an implementation detail. + +- (void)setCacheChild:(id)obj forKey:(NSString *)key { + if (_childCache == nil && obj != nil) { + _childCache = [[NSMutableDictionary alloc] initWithObjectsAndKeys: + obj, key, nil]; + } else { + [_childCache setValue:obj forKey:key]; + } +} + +- (id)cacheChildForKey:(NSString *)key { + id obj = [_childCache objectForKey:key]; + return obj; +} + +#pragma mark Support methods + ++ (NSMutableArray *)allDeclaredProperties { + NSMutableArray *array = [NSMutableArray array]; + + // walk from this class up the hierarchy to GTLRObject + Class topClass = class_getSuperclass([GTLRObject class]); + for (Class currClass = self; + currClass != topClass; + currClass = class_getSuperclass(currClass)) { + // step through this class's properties, and add the property names to the + // array + objc_property_t *properties = class_copyPropertyList(currClass, NULL); + if (properties) { + for (objc_property_t *prop = properties; + *prop != NULL; + ++prop) { + const char *propName = property_getName(*prop); + // We only want dynamic properties; their attributes contain ",D". + const char *attr = property_getAttributes(*prop); + const char *dynamicMarker = strstr(attr, ",D"); + if (dynamicMarker && + (dynamicMarker[2] == 0 || dynamicMarker[2] == ',' )) { + [array addObject:(id _Nonnull)@(propName)]; + } + } + free(properties); + } + } + return array; +} + ++ (NSArray *)allKnownKeys { + NSArray *allProps = [self allDeclaredProperties]; + NSMutableArray *knownKeys = [NSMutableArray arrayWithArray:allProps]; + + NSDictionary *propMap = [GTLRObject propertyToJSONKeyMapForClass:[self class]]; + + NSUInteger idx = 0; + for (NSString *propName in allProps) { + NSString *jsonKey = [propMap objectForKey:propName]; + if (jsonKey) { + [knownKeys replaceObjectAtIndex:idx + withObject:jsonKey]; + } + ++idx; + } + return knownKeys; +} + +- (NSString *)description { + NSString *jsonDesc = [self JSONDescription]; + + NSString *str = [NSString stringWithFormat:@"%@ %p: %@", + [self class], self, jsonDesc]; + return str; +} + +// Internal utility for creating an appropriate description summary for the object's JSON. +- (NSString *)JSONDescription { + // Find the list of declared and otherwise known JSON keys for this class. + NSArray *knownKeys = [[self class] allKnownKeys]; + + NSMutableString *descStr = [NSMutableString stringWithString:@"{"]; + + NSString *spacer = @""; + for (NSString *key in [[_json allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { + NSString *value = nil; + // show question mark for JSON keys not supported by a declared property: + // foo?:"Hi mom." + NSString *qmark = [knownKeys containsObject:key] ? @"" : @"?"; + + // determine property value to dislay + id rawValue = [_json valueForKey:key]; + if ([rawValue isKindOfClass:[NSDictionary class]]) { + // for dictionaries, show the list of keys: + // {key1,key2,key3} + NSArray *subKeys = [((NSDictionary *)rawValue).allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + NSString *subkeyList = [subKeys componentsJoinedByString:@","]; + value = [NSString stringWithFormat:@"{%@}", subkeyList]; + } else if ([rawValue isKindOfClass:[NSArray class]]) { + // for arrays, show the number of items in the array: + // [3] + value = [NSString stringWithFormat:@"[%tu]", ((NSArray *)rawValue).count]; + } else if ([rawValue isKindOfClass:[NSString class]]) { + // for strings, show the string in quotes: + // "Hi mom." + value = [NSString stringWithFormat:@"\"%@\"", rawValue]; + } else { + // for numbers, show just the number + value = [rawValue description]; + } + [descStr appendFormat:@"%@%@%@:%@", spacer, key, qmark, value]; + spacer = @" "; + } + [descStr appendString:@"}"]; + return descStr; +} + +#pragma mark Object Instantiation + ++ (GTLRObject *)objectForJSON:(NSMutableDictionary *)json + defaultClass:(Class)defaultClass + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver { + if (((id)json == [NSNull null]) || json.count == 0) { + if (json != nil && defaultClass != Nil) { + // The JSON included an empty dictionary, just create the object. + Class classToCreate = + GTLRObjectResolveClass(objectClassResolver, + [NSDictionary dictionary], + defaultClass); + return [classToCreate object]; + } + // No actual result, such as the response from a delete. + return nil; + } + + if (defaultClass == Nil) { + defaultClass = self; + } + + Class classToCreate = + GTLRObjectResolveClass(objectClassResolver, json, defaultClass); + + // now instantiate the GTLRObject + GTLRObject *parsedObject = [classToCreate object]; + parsedObject.objectClassResolver = objectClassResolver; + parsedObject.JSON = json; + return parsedObject; +} + +#pragma mark Runtime Utilities + +static NSMutableDictionary *gJSONKeyMapCache = nil; +static NSMutableDictionary *gArrayPropertyToClassMapCache = nil; + ++ (void)initialize { + // Note that initialize is guaranteed by the runtime to be called in a + // thread-safe manner + if (gJSONKeyMapCache == nil) { + gJSONKeyMapCache = [[NSMutableDictionary alloc] init]; + } + if (gArrayPropertyToClassMapCache == nil) { + gArrayPropertyToClassMapCache = [[NSMutableDictionary alloc] init]; + } +} + ++ (NSDictionary *)propertyToJSONKeyMapForClass:(Class<GTLRRuntimeCommon>)aClass { + NSDictionary *resultMap = + [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(propertyToJSONKeyMap) + startClass:aClass + ancestorClass:[GTLRObject class] + cache:gJSONKeyMapCache]; + return resultMap; +} + ++ (NSDictionary *)arrayPropertyToClassMapForClass:(Class<GTLRRuntimeCommon>)aClass { + NSDictionary *resultMap = + [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap) + startClass:aClass + ancestorClass:[GTLRObject class] + cache:gArrayPropertyToClassMapCache]; + return resultMap; +} + +#pragma mark Runtime Support + ++ (Class<GTLRRuntimeCommon>)ancestorClass { + return [GTLRObject class]; +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + BOOL resolved = [GTLRRuntimeCommon resolveInstanceMethod:sel onClass:self]; + if (resolved) + return YES; + + return [super resolveInstanceMethod:sel]; +} + +@end + +@implementation GTLRCollectionObject + ++ (NSString *)collectionItemsKey { + // GTLRCollectionObject fast enumeration, indexed access, and automatic pagination + // (when shouldFetchNextPages is enabled) applies to the object array property "items". + // The array property's key may be different if subclasses override this method. + return @"items"; +} + +- (id)objectAtIndexedSubscript:(NSUInteger)idx { + NSString *key = [[self class] collectionItemsKey]; + NSArray *items = [self valueForKey:key]; + if (items == nil) { + [NSException raise:NSRangeException + format:@"index %tu beyond bounds (%@ property \"%@\" is nil)", + idx, [self class], key]; + } + id result = [items objectAtIndexedSubscript:idx]; + return result; +} + +// NSFastEnumeration protocol +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unsafe_unretained id _Nonnull *)stackbuf + count:(NSUInteger)len { + NSString *key = [[self class] collectionItemsKey]; + NSArray *items = [self valueForKey:key]; + NSUInteger result = [items countByEnumeratingWithState:state + objects:stackbuf + count:len]; + return result; +} + +@end + +@implementation GTLRDataObject + +@synthesize data = _data, + contentType = _contentType; + +- (NSString *)description { + NSString *jsonDesc = @""; + if (self.JSON.count > 0) { + jsonDesc = [self JSONDescription]; + } + return [NSString stringWithFormat:@"%@ %p: %tu bytes, contentType:%@ %@", + [self class], self, self.data.length, self.contentType, jsonDesc]; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLRDataObject *newObj = [super copyWithZone:zone]; + newObj.data = [self.data copy]; + newObj.contentType = self.contentType; + return newObj; +} + +@end + +@implementation GTLRResultArray + +- (NSArray *)itemsWithItemClass:(Class)itemClass { + // Return the cached array before creating on demand. + NSString *cacheKey = @"result_array_items"; + NSMutableArray *cachedArray = [self cacheChildForKey:cacheKey]; + if (cachedArray != nil) { + return cachedArray; + } + NSArray *result = nil; + NSArray *array = (NSArray *)self.JSON; + if (array != nil) { + if ([array isKindOfClass:[NSArray class]]) { + id<GTLRObjectClassResolver>objectClassResolver = self.objectClassResolver; + result = [GTLRRuntimeCommon objectFromJSON:array + defaultClass:itemClass + objectClassResolver:objectClassResolver + isCacheable:NULL]; + } else { +#if DEBUG + if (![array isKindOfClass:[NSNull class]]) { + GTLR_DEBUG_LOG(@"GTLRObject: unexpected JSON: %@ should be an array, actually is a %@:\n%@", + NSStringFromClass([self class]), + NSStringFromClass([array class]), + array); + } +#endif + result = array; + } + } + + [self setCacheChild:result forKey:cacheKey]; + return result; +} + +- (NSString *)JSONDescription { + // Just like GTLRObject's handing of arrays, just return the count. + return [NSString stringWithFormat:@"[%tu]", self.JSON.count]; +} + +@end + +Class GTLRObjectResolveClass( + id<GTLRObjectClassResolver>objectClassResolver, + NSDictionary *json, + Class defaultClass) { + Class result = [objectClassResolver classForJSON:json + defaultClass:defaultClass]; + if (result == Nil) { + result = defaultClass; + } + return result; +} + +@implementation GTLRObjectClassResolver { + NSDictionary<NSString *, Class> *_kindToClassMap; + NSDictionary<Class, Class> *_surrogates; +} + ++ (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap { + GTLRObjectClassResolver *result = [[self alloc] initWithKindMap:kindStringToClassMap + surrogates:nil]; + return result; +} + ++ (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap + surrogates:(NSDictionary<Class, Class> *)surrogates { + GTLRObjectClassResolver *result = [[self alloc] initWithKindMap:kindStringToClassMap + surrogates:surrogates]; + return result; +} + +- (instancetype)initWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap + surrogates:(NSDictionary<Class, Class> *)surrogates { + self = [super init]; + if (self) { + _kindToClassMap = [kindStringToClassMap copy]; + _surrogates = [surrogates copy]; + } + return self; +} + +- (Class)classForJSON:(NSDictionary *)json + defaultClass:(Class)defaultClass { + Class result = defaultClass; + + // Apply kind map. + BOOL shouldUseKind = (result == Nil) || [result isKindValidForClassRegistry]; + if (shouldUseKind && [json isKindOfClass:[NSDictionary class]]) { + NSString *kind = [json valueForKey:@"kind"]; + if ([kind isKindOfClass:[NSString class]] && kind.length > 0) { + Class dynamicClass = [_kindToClassMap objectForKey:kind]; + if (dynamicClass) { + result = dynamicClass; + } + } + } + + // Apply surrogate map. + Class surrogate = [_surrogates objectForKey:result]; + if (surrogate) { + result = surrogate; + } + + return result; +} + +@end + +static NSMutableDictionary *DeepMutableCopyOfJSONDictionary(NSDictionary *initialJSON) { + if (!initialJSON) return nil; + + NSMutableDictionary *result; + CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, + (__bridge CFPropertyListRef)(initialJSON), + kCFPropertyListMutableContainers); + if (ref) { + result = CFBridgingRelease(ref); + } else { + // Failed to copy, probably due to a non-plist type such as NSNull. + // + // As a fallback, round-trip through NSJSONSerialization. + NSError *serializationError; + NSData *data = [NSJSONSerialization dataWithJSONObject:initialJSON + options:0 + error:&serializationError]; + if (!data) { + GTLR_DEBUG_ASSERT(0, @"Copy failed due to serialization: %@\nJSON: %@", + serializationError, initialJSON); + } else { + result = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&serializationError]; + GTLR_DEBUG_ASSERT(result != nil, @"Copy failed due to deserialization: %@\nJSON: %@", + serializationError, + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + } + } + return result; +} diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.h @@ -0,0 +1,253 @@ +/* Copyright (c) 2011 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. + */ + +// Query documentation: +// https://github.com/google/google-api-objectivec-client-for-rest/wiki#query-operations + +#import "GTLRObject.h" +#import "GTLRUploadParameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GTLRServiceTicket; +@class GTLRServiceExecutionParameters; +@class GTLRQuery; + +/** + * This protocol is just to support passing of either a batch or a single query + * to a GTLRService instance. The library does not expect or support client app + * implementations of this protocol. + */ +@protocol GTLRQueryProtocol <NSObject, NSCopying> + +/** + * Service ticket values may be set in the execution parameters for an individual query + * prior to executing the query. + */ +@property(atomic, strong, null_resettable) GTLRServiceExecutionParameters *executionParameters; + +- (BOOL)isBatchQuery; +- (BOOL)hasExecutionParameters; +- (BOOL)shouldSkipAuthorization; +- (void)invalidateQuery; +- (nullable NSDictionary<NSString *, NSString *> *)additionalHTTPHeaders; +- (nullable NSDictionary<NSString *, NSString *> *)additionalURLQueryParameters; +- (nullable NSString *)loggingName; +- (nullable GTLRUploadParameters *)uploadParameters; + +@end + +@protocol GTLRQueryCollectionProtocol +@optional +@property(nonatomic, strong) NSString *pageToken; +@end + +/** + * A block called when a query completes executing. + * + * Errors passed to the completionBlock will have an "underlying" GTLRErrorObject + * when the server returned an error for this specific query: + * + * GTLRErrorObject *errorObj = [GTLRErrorObject underlyingObjectForError:callbackError]; + * if (errorObj) { + * // The server returned this error for this specific query. + * } else { + * // The query execution fetch failed. + * } + * + * @param callbackTicket The ticket that tracked query execution. + * @param object The result of query execution. This will be derived from + * GTLRObject. + * @param callbackError If non-nil, the query execution failed. + */ +typedef void (^GTLRQueryCompletionBlock)(GTLRServiceTicket *callbackTicket, + id _Nullable object, + NSError * _Nullable callbackError); + +/** + * Class for a single query. + */ +@interface GTLRQuery : NSObject <GTLRQueryProtocol, NSCopying> + +/** + * The object to be uploaded with the query. The JSON of this object becomes + * the body for PUT and POST requests. + */ +@property(atomic, strong, nullable) GTLRObject *bodyObject; + +/** + * Each query must have a request ID string. The client app may replace the + * default assigned request ID with a custom string, provided that if + * used in a batch query, all request IDs in the batch must be unique. + */ +@property(atomic, copy) NSString *requestID; + +/** + * For queries which support file upload, the MIME type and file URL + * or data must be provided. + */ +@property(atomic, copy, nullable) GTLRUploadParameters *uploadParameters; + +/** + * Any additional URL query parameters for this query. + * + * These query parameters override the same keys from the service object's + * additionalURLQueryParameters + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalURLQueryParameters; + +/** + * Any additional HTTP headers for this query. + * + * These headers override the same keys from the service object's additionalHTTPHeaders + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalHTTPHeaders; + +/** + * If set, when the query is executed, an @c "alt" query parameter is added + * with this value and the raw result of the query is returned in a + * GTLRDataObject. This is useful when the server documents result datatypes + * other than JSON ("csv", for example). + */ +@property(atomic, copy) NSString *downloadAsDataObjectType; + +/** + * If set, and the query also has a non-empty @c downloadAsDataObjectType, the + * URL to download from will be modified to include "download/". This extra path + * component avoids the need for a server redirect to the download URL. + */ +@property(atomic, assign) BOOL useMediaDownloadService; + +/** + * Clients may set this to YES to disallow authorization. Defaults to NO. + */ +@property(atomic, assign) BOOL shouldSkipAuthorization; + +/** + * An optional callback block to be called immediately before the executeQuery: completion handler. + * + * The completionBlock property is particularly useful for queries executed in a batch. + */ +@property(atomic, copy, nullable) GTLRQueryCompletionBlock completionBlock; + +/** + * The brief string to identify this query in GTMSessionFetcher http logs. + * + * A default logging name is set by the code generator, but may be overridden by the client app. + */ +@property(atomic, copy, nullable) NSString *loggingName; + +#pragma mark Internal +///////////////////////////////////////////////////////////////////////////////////////////// +// +// Properties below are used by the library and aren't typically needed by client apps. +// +///////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The URITemplate path segment. This is initialized in by the service generator. + */ +@property(atomic, readonly) NSString *pathURITemplate; + +/** + * The HTTP method to use for this query. This is initialized in by the service generator. + */ +@property(atomic, readonly, nullable) NSString *httpMethod; + +/** + * The parameters names that are in the URI Template. + * This is initialized in by the service generator. + * + * The service generator collects these via the discovery info instead of having to parse the + * template to figure out what is part of the path. + */ +@property(atomic, readonly, nullable) NSArray<NSString *> *pathParameterNames; + +/** + * The JSON dictionary of all the parameters set on this query. + * + * The JSON values are set by setting the query's properties. + */ +@property(nonatomic, strong, nullable) NSMutableDictionary<NSString *, id> *JSON; + +/** + * A custom URI template for resumable uploads. This is initialized by the service generator + * if needed. + */ +@property(atomic, copy, nullable) NSString *resumableUploadPathURITemplateOverride; + +/** + * A custom URI template for simple and multipart media uploads. This is initialized + * by the service generator. + */ +@property(atomic, copy, nullable) NSString *simpleUploadPathURITemplateOverride; + +/** + * The GTLRObject subclass expected for results. This is initialized by the service generator. + * + * This is needed if the object returned by the server lacks a known "kind" string. + */ +@property(atomic, assign, nullable) Class expectedObjectClass; + +/** + * Set when the query has been invalidated, meaning it was slated for execution so it's been copied + * and its callbacks were released, or it's a copy that has finished executing. + * + * Once a query has been invalidated, it cannot be executed, added to a batch, or copied. + */ +@property(atomic, assign, getter=isQueryInvalid) BOOL queryInvalid; + +/** + * Internal query init method. + * + * @param pathURITemplate URI template to be filled in with parameters. + * @param httpMethod The requests's http method. A nil method will execute as GET. + * @param pathParameterNames Names of parameters to be replaced in the template. + */ +- (instancetype)initWithPathURITemplate:(NSString *)pathURITemplate + HTTPMethod:(nullable NSString *)httpMethod + pathParameterNames:(nullable NSArray<NSString *> *)pathParameterNames NS_DESIGNATED_INITIALIZER; + +/** + * @return Auto-generated request ID string. + */ ++ (NSString *)nextRequestID; + +/** + * Overridden by subclasses. + * + * @return Substitute parameter names where needed for Objective-C or library compatibility. + */ ++ (nullable NSDictionary<NSString *, NSString *> *)parameterNameMap; + +/** + * Overridden by subclasses. + * + * @return Map of property keys to specifying the class of objects to be instantiated in arrays. + */ ++ (nullable NSDictionary<NSString *, Class> *)arrayPropertyToClassMap; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +/** + * The library doesn't use GTLRQueryCollectionImpl, but it provides a concrete implementation + * of the protocol so the methods do not cause private method errors in Xcode/AppStore review. + */ +@interface GTLRQueryCollectionImpl : GTLRQuery <GTLRQueryCollectionProtocol> +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRQuery.m @@ -0,0 +1,313 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#include <objc/runtime.h> + +#import "GTLRQuery.h" +#import "GTLRRuntimeCommon.h" +#import "GTLRService.h" +#import "GTLRUtilities.h" + +@interface GTLRQuery () <GTLRRuntimeCommon> +@end + +@implementation GTLRQuery { + NSMutableDictionary *_childCache; + GTLRServiceExecutionParameters *_executionParameters; +} + +@synthesize additionalURLQueryParameters = _additionalURLQueryParameters, + additionalHTTPHeaders = _additionalHTTPHeaders, + bodyObject = _bodyObject, + completionBlock = _completionBlock, + downloadAsDataObjectType = _downloadAsDataObjectType, + expectedObjectClass = _expectedObjectClass, + httpMethod = _httpMethod, + JSON = _json, + loggingName = _loggingName, + pathParameterNames = _pathParameterNames, + pathURITemplate = _pathURITemplate, + queryInvalid = _queryInvalid, + requestID = _requestID, + resumableUploadPathURITemplateOverride = _resumableUploadPathURITemplateOverride, + shouldSkipAuthorization = _shouldSkipAuthorization, + simpleUploadPathURITemplateOverride = _simpleUploadPathURITemplateOverride, + uploadParameters = _uploadParameters, + useMediaDownloadService = _useMediaDownloadService; + +#if DEBUG +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + self = nil; + return self; +} +#endif + +- (instancetype)initWithPathURITemplate:(NSString *)pathURITemplate + HTTPMethod:(nullable NSString *)httpMethod + pathParameterNames:(nullable NSArray<NSString *> *)pathParameterNames { + self = [super init]; + if (self) { + _requestID = [[self class] nextRequestID]; + + _pathURITemplate = [pathURITemplate copy]; + _httpMethod = [httpMethod copy]; + _pathParameterNames = [pathParameterNames copy]; + + if (_pathURITemplate.length == 0) { + self = nil; + } + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLR_DEBUG_ASSERT(!self.queryInvalid, @"Cannot copy an executed query: %@", self); + + GTLRQuery *query = + [[[self class] allocWithZone:zone] initWithPathURITemplate:self.pathURITemplate + HTTPMethod:self.httpMethod + pathParameterNames:self.pathParameterNames]; + + if (_json.count > 0) { + // Deep copy the parameters + CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, + (__bridge CFPropertyListRef)(_json), + kCFPropertyListMutableContainers); + query.JSON = CFBridgingRelease(ref); + } + + // Using the executionParameters ivar avoids creating the object. + query.executionParameters = self.executionParameters; + + // Copied in the same order as synthesized above. + query.additionalHTTPHeaders = self.additionalHTTPHeaders; + query.additionalURLQueryParameters = self.additionalURLQueryParameters; + query.bodyObject = self.bodyObject; + query.completionBlock = self.completionBlock; + query.downloadAsDataObjectType = self.downloadAsDataObjectType; + query.expectedObjectClass = self.expectedObjectClass; + // http method passed to init above. + // JSON copied above. + query.loggingName = self.loggingName; + // pathParameterNames passed to init above. + // pathURITemplate passed to init above. + query.queryInvalid = self.queryInvalid; + query.requestID = self.requestID; + query.resumableUploadPathURITemplateOverride = self.resumableUploadPathURITemplateOverride; + query.shouldSkipAuthorization = self.shouldSkipAuthorization; + query.simpleUploadPathURITemplateOverride = self.simpleUploadPathURITemplateOverride; + query.uploadParameters = self.uploadParameters; + query.useMediaDownloadService = self.useMediaDownloadService; + + return query; +} + +#if DEBUG +- (NSString *)description { + NSArray *keys = self.JSON.allKeys; + NSArray *params = [keys sortedArrayUsingSelector:@selector(compare:)]; + NSString *paramsSummary = @""; + if (params.count > 0) { + paramsSummary = [NSString stringWithFormat:@" params:(%@)", + [params componentsJoinedByString:@","]]; + } + + NSString *invalidStr = @""; + if (self.queryInvalid) { + invalidStr = @" [callbacks released]"; + } + + keys = self.additionalURLQueryParameters.allKeys; + NSArray *urlQParams = [keys sortedArrayUsingSelector:@selector(compare:)]; + NSString *urlQParamsSummary = @""; + if (urlQParams.count > 0) { + urlQParamsSummary = [NSString stringWithFormat:@" urlQParams:(%@)", + [urlQParams componentsJoinedByString:@","]]; + } + + GTLRObject *bodyObj = self.bodyObject; + NSString *bodyObjSummary = @""; + if (bodyObj != nil) { + bodyObjSummary = [NSString stringWithFormat:@" bodyObject:%@", [bodyObj class]]; + } + + NSString *uploadStr = @""; + GTLRUploadParameters *uploadParams = self.uploadParameters; + if (uploadParams) { + uploadStr = [NSString stringWithFormat:@" %@", uploadParams]; + } + + NSString *httpMethod = self.httpMethod; + if (httpMethod == nil) { + httpMethod = @"GET"; + } + + NSString *dataObjectType = self.downloadAsDataObjectType; + NSString *downloadStr = @""; + if (dataObjectType.length > 0) { + downloadStr = + [NSString stringWithFormat:@" downloadDataAs:%@", dataObjectType]; + } + + return [NSString stringWithFormat:@"%@ %p:%@%@ {%@ pathTemplate:%@%@%@%@%@}", + [self class], self, invalidStr, downloadStr, + httpMethod, self.pathURITemplate, + paramsSummary, urlQParamsSummary, bodyObjSummary, uploadStr]; +} +#endif // DEBUG + +- (BOOL)isBatchQuery { + return NO; +} + +- (void)invalidateQuery { + self.queryInvalid = YES; + self.completionBlock = nil; + self.executionParameters = nil; +} + +- (GTLRServiceExecutionParameters *)executionParameters { + @synchronized(self) { + if (!_executionParameters) { + _executionParameters = [[GTLRServiceExecutionParameters alloc] init]; + } + return _executionParameters; + } +} + +- (void)setExecutionParameters:(nullable GTLRServiceExecutionParameters *)executionParameters { + @synchronized(self) { + _executionParameters = executionParameters; + } +} + +- (BOOL)hasExecutionParameters { + return self.executionParameters.hasParameters; +} + ++ (NSString *)nextRequestID { + static NSUInteger lastRequestID = 0; + NSString *result; + + @synchronized([GTLRQuery class]) { + ++lastRequestID; + result = [NSString stringWithFormat:@"gtlr_%tu", lastRequestID]; + } + return result; +} + +#pragma mark GTLRRuntimeCommon Support + +- (void)setJSONValue:(id)obj forKey:(NSString *)key { + NSMutableDictionary *dict = self.JSON; + if (dict == nil && obj != nil) { + dict = [NSMutableDictionary dictionaryWithCapacity:1]; + self.JSON = dict; + } + [dict setValue:obj forKey:key]; +} + +- (id)JSONValueForKey:(NSString *)key { + id obj = [self.JSON objectForKey:key]; + return obj; +} + +// There is no property for _childCache as there shouldn't be KVC/KVO +// support for it, since it's an implementation detail. + +- (void)setCacheChild:(id)obj forKey:(NSString *)key { + if (_childCache == nil && obj != nil) { + _childCache = [[NSMutableDictionary alloc] initWithObjectsAndKeys:obj, key, nil]; + } else { + [_childCache setValue:obj forKey:key]; + } +} + +- (id)cacheChildForKey:(NSString *)key { + id obj = [_childCache objectForKey:key]; + return obj; +} + +#pragma mark Methods for Subclasses to Override + ++ (NSDictionary<NSString *, NSString *> *)parameterNameMap { + return nil; +} + ++ (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap { + return nil; +} + +#pragma mark Runtime Utilities + +static NSMutableDictionary *gQueryParameterNameMapCache = nil; +static NSMutableDictionary *gQueryArrayPropertyToClassMapCache = nil; + ++ (void)initialize { + // Note that +initialize is guaranteed by the runtime to be called in a thread-safe manner. + if (gQueryParameterNameMapCache == nil) { + gQueryParameterNameMapCache = [[NSMutableDictionary alloc] init]; + } + if (gQueryArrayPropertyToClassMapCache == nil) { + gQueryArrayPropertyToClassMapCache = [[NSMutableDictionary alloc] init]; + } +} + ++ (NSDictionary *)propertyToJSONKeyMapForClass:(Class<GTLRRuntimeCommon>)aClass { + NSDictionary *resultMap = + [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(parameterNameMap) + startClass:aClass + ancestorClass:[GTLRQuery class] + cache:gQueryParameterNameMapCache]; + return resultMap; +} + ++ (NSDictionary *)arrayPropertyToClassMapForClass:(Class<GTLRRuntimeCommon>)aClass { + NSDictionary *resultMap = + [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap) + startClass:aClass + ancestorClass:[GTLRQuery class] + cache:gQueryArrayPropertyToClassMapCache]; + return resultMap; +} + +#pragma mark Runtime Support + +- (id<GTLRObjectClassResolver>)objectClassResolver { + // Stub method just needed for RuntimeCommon. + return nil; +} + ++ (Class<GTLRRuntimeCommon>)ancestorClass { + return [GTLRQuery class]; +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + BOOL resolved = [GTLRRuntimeCommon resolveInstanceMethod:sel onClass:self]; + if (resolved) return YES; + + return [super resolveInstanceMethod:sel]; +} + +@end + +@implementation GTLRQueryCollectionImpl +@dynamic pageToken; +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2011 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 <Foundation/Foundation.h> + +#import "GTLRDefines.h" + +@protocol GTLRObjectClassResolver; + +NS_ASSUME_NONNULL_BEGIN + +// This protocol and support class are an internal implementation detail so +// GTLRObject and GTLRQuery can share some code. + +/** + * An internal protocol for the GTLR library. + * + * None of these methods should be used by client apps. + */ +@protocol GTLRRuntimeCommon <NSObject> +@required +// Get/Set properties +- (void)setJSONValue:(nullable id)obj forKey:(NSString *)key; +- (id)JSONValueForKey:(NSString *)key; +// Child cache +- (void)setCacheChild:(nullable id)obj forKey:(NSString *)key; +- (nullable id)cacheChildForKey:(NSString *)key; +// Object mapper. +- (nullable id<GTLRObjectClassResolver>)objectClassResolver; +// Key map ++ (nullable NSDictionary<NSString *, NSString *> *)propertyToJSONKeyMapForClass:(Class<GTLRRuntimeCommon>)aClass; +// Array item types ++ (nullable NSDictionary<NSString *, Class> *)arrayPropertyToClassMapForClass:(Class<GTLRRuntimeCommon>)aClass; +// The parent class for dynamic support ++ (nullable Class<GTLRRuntimeCommon>)ancestorClass; +@end + +/** + * An internal class for the GTLR library. + * + * None of these methods should be used by client apps. + */ +@interface GTLRRuntimeCommon : NSObject +// Wire things up. ++ (BOOL)resolveInstanceMethod:(SEL)sel onClass:(Class)onClass; +// Helpers ++ (nullable id)objectFromJSON:(id)json + defaultClass:(nullable Class)defaultClass + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver + isCacheable:(nullable BOOL *)isCacheable; ++ (nullable id)jsonFromAPIObject:(id)obj + expectedClass:(nullable Class)expectedClass + isCacheable:(nullable BOOL *)isCacheable; +// Walk up the class tree merging dictionaries and return the result. ++ (NSDictionary *)mergedClassDictionaryForSelector:(SEL)selector + startClass:(Class)startClass + ancestorClass:(Class)ancestorClass + cache:(NSMutableDictionary *)cache; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRRuntimeCommon.m @@ -0,0 +1,1060 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#include <objc/runtime.h> +#include <TargetConditionals.h> + +#import "GTLRRuntimeCommon.h" + +#import "GTLRDateTime.h" +#import "GTLRDuration.h" +#import "GTLRObject.h" +#import "GTLRUtilities.h" + +// Note: NSObject's class is used as a marker for the expected/default class +// when Discovery says it can be any type of object. + +@implementation GTLRRuntimeCommon + +// Helper to generically convert JSON to an api object type. ++ (id)objectFromJSON:(id)json + defaultClass:(Class)defaultClass + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver + isCacheable:(BOOL*)isCacheable { + id result = nil; + BOOL canBeCached = YES; + + // TODO(TVL): use defaultClass to validate things like expectedClass is + // done in jsonFromAPIObject:expectedClass:isCacheable:? + + if ([json isKindOfClass:[NSDictionary class]]) { + // If no default, or the default was any object, then default to base + // object here (and hope there is a kind to get the right thing). + if ((defaultClass == Nil) || [defaultClass isEqual:[NSObject class]]) { + defaultClass = [GTLRObject class]; + } + result = [GTLRObject objectForJSON:json + defaultClass:defaultClass + objectClassResolver:objectClassResolver]; + } else if ([json isKindOfClass:[NSArray class]]) { + NSArray *jsonArray = json; + // make an object for each JSON dictionary in the array + NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:jsonArray.count]; + for (id jsonItem in jsonArray) { + id item = [self objectFromJSON:jsonItem + defaultClass:defaultClass + objectClassResolver:objectClassResolver + isCacheable:NULL]; + [resultArray addObject:item]; + } + result = resultArray; + } else if ([json isKindOfClass:[NSString class]]) { + // DateTimes and Durations live in JSON as strings, so convert. + if ([defaultClass isEqual:[GTLRDateTime class]]) { + result = [GTLRDateTime dateTimeWithRFC3339String:json]; + } else if ([defaultClass isEqual:[GTLRDuration class]]) { + result = [GTLRDuration durationWithJSONString:json]; + } else if ([defaultClass isEqual:[NSNumber class]]) { + result = GTLR_EnsureNSNumber(json); + canBeCached = NO; + } else { + result = json; + canBeCached = NO; + } + } else if ([json isKindOfClass:[NSNumber class]] || + [json isKindOfClass:[NSNull class]]) { + result = json; + canBeCached = NO; + } else { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: unsupported class '%s' in objectFromJSON", + class_getName([json class])); + } + + if (isCacheable) { + *isCacheable = canBeCached; + } + return result; +} + +// Helper to generically convert an api object type to JSON. +// |expectedClass| is the type that was expected for |obj|. ++ (id)jsonFromAPIObject:(id)obj + expectedClass:(Class)expectedClass + isCacheable:(BOOL *)isCacheable { + id result = nil; + BOOL canBeCached = YES; + BOOL checkExpected = (expectedClass != Nil); + + if ([obj isKindOfClass:[NSString class]]) { + result = [obj copy]; + canBeCached = NO; + } else if ([obj isKindOfClass:[NSNumber class]] || + [obj isKindOfClass:[NSNull class]]) { + result = obj; + canBeCached = NO; + } else if ([obj isKindOfClass:[GTLRObject class]]) { + result = [(GTLRObject *)obj JSON]; + if (result == nil) { + // adding an empty object; it should have a JSON dictionary so it can + // hold future assignments + [(GTLRObject *)obj setJSON:[NSMutableDictionary dictionary]]; + result = [(GTLRObject *)obj JSON]; + } + } else if ([obj isKindOfClass:[NSArray class]]) { + checkExpected = NO; + NSArray *array = obj; + // get the JSON for each thing in the array + NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:array.count]; + for (id item in array) { + id itemJSON = [self jsonFromAPIObject:item + expectedClass:expectedClass + isCacheable:NULL]; + [resultArray addObject:itemJSON]; + } + result = resultArray; + } else if ([obj isKindOfClass:[GTLRDateTime class]]) { + // DateTimes live in JSON as strings, so convert. + GTLRDateTime *dateTime = obj; + result = dateTime.RFC3339String; + } else if ([obj isKindOfClass:[GTLRDuration class]]) { + // Durations live in JSON as strings, so convert. + GTLRDuration *duration = obj; + result = duration.jsonString; + } else { + checkExpected = NO; + if (obj) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: unsupported class '%s' in jsonFromAPIObject", + class_getName([obj class])); + } + } + + if (checkExpected) { + // If the default was any object, then clear it to skip validation checks. + if ([expectedClass isEqual:[NSObject class]] || + [obj isKindOfClass:[NSNull class]]) { + expectedClass = nil; + } + if (expectedClass && ![obj isKindOfClass:expectedClass]) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: jsonFromAPIObject expected class '%s' instead got '%s'", + class_getName(expectedClass), class_getName([obj class])); + } + } + + if (isCacheable) { + *isCacheable = canBeCached; + } + return result; +} + ++ (NSDictionary *)mergedClassDictionaryForSelector:(SEL)selector + startClass:(Class)startClass + ancestorClass:(Class)ancestorClass + cache:(NSMutableDictionary *)cache { + NSDictionary *result; + @synchronized(cache) { + result = [cache objectForKey:startClass]; + if (result == nil) { + // Collect the class's dictionary. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + NSDictionary *classDict = [startClass performSelector:selector]; +#pragma clang diagnostic pop + + // Collect the parent class's merged dictionary. + NSDictionary *parentClassMergedDict; + if ([startClass isEqual:ancestorClass]) { + parentClassMergedDict = nil; + } else { + Class parentClass = class_getSuperclass(startClass); + parentClassMergedDict = + [self mergedClassDictionaryForSelector:selector + startClass:parentClass + ancestorClass:ancestorClass + cache:cache]; + } + + // Merge this class's into the parent's so things properly override. + NSMutableDictionary *mergeDict; + if (parentClassMergedDict != nil) { + mergeDict = + [NSMutableDictionary dictionaryWithDictionary:parentClassMergedDict]; + } else { + mergeDict = [NSMutableDictionary dictionary]; + } + if (classDict != nil) { + [mergeDict addEntriesFromDictionary:classDict]; + } + + // Make an immutable version. + result = [NSDictionary dictionaryWithDictionary:mergeDict]; + + // Save it. + [cache setObject:result forKey:(id<NSCopying>)startClass]; + } + } + return result; +} + +#pragma mark Runtime lookup support + +static objc_property_t PropertyForSel(Class<GTLRRuntimeCommon> startClass, + SEL sel, BOOL isSetter, + Class<GTLRRuntimeCommon> *outFoundClass) { + const char *selName = sel_getName(sel); + const char *baseName = selName; + size_t baseNameLen = strlen(baseName); + if (isSetter) { + baseName += 3; // skip "set" + baseNameLen -= 4; // subtract "set" and the final colon + } + + // walk from this class up the hierarchy to the ancestor class + Class<GTLRRuntimeCommon> topClass = class_getSuperclass([startClass ancestorClass]); + for (Class currClass = startClass; + currClass != topClass; + currClass = class_getSuperclass(currClass)) { + // step through this class's properties + objc_property_t foundProp = NULL; + objc_property_t *properties = class_copyPropertyList(currClass, NULL); + if (properties) { + for (objc_property_t *prop = properties; *prop != NULL; ++prop) { + const char *propAttrs = property_getAttributes(*prop); + const char *dynamicMarker = strstr(propAttrs, ",D"); + if (!dynamicMarker || + (dynamicMarker[2] != 0 && dynamicMarker[2] != ',' )) { + // It isn't dynamic, skip it. + continue; + } + + if (!isSetter) { + // See if this property has an explicit getter=. (the attributes always start with a T, + // so we can check for the leading ','. + const char *getterMarker = strstr(propAttrs, ",G"); + if (getterMarker) { + const char *getterStart = getterMarker + 2; + const char *getterEnd = getterStart; + while ((*getterEnd != 0) && (*getterEnd != ',')) { + ++getterEnd; + } + size_t getterLen = (size_t)(getterEnd - getterStart); + if ((strncmp(selName, getterStart, getterLen) == 0) + && (selName[getterLen] == 0)) { + // return the actual property + foundProp = *prop; + // if requested, return the class containing the property + if (outFoundClass) *outFoundClass = currClass; + break; + } + } // if (getterMarker) + } // if (!isSetter) + + // Search for an exact-name match (a getter), but case-insensitive on the + // first character (in case baseName comes from a setter) + const char *propName = property_getName(*prop); + size_t propNameLen = strlen(propName); + if (baseNameLen == propNameLen + && strncasecmp(baseName, propName, 1) == 0 + && (baseNameLen <= 1 + || strncmp(baseName + 1, propName + 1, baseNameLen - 1) == 0)) { + // return the actual property + foundProp = *prop; + + // if requested, return the class containing the property + if (outFoundClass) *outFoundClass = currClass; + break; + } + } // for (prop in properties) + free(properties); + } + if (foundProp) return foundProp; + } + + // not found; this occasionally happens when the system looks for a method + // like "getFoo" or "descriptionWithLocale:indent:" + return NULL; +} + +typedef NS_ENUM(NSUInteger, GTLRPropertyType) { +#if !defined(__LP64__) || !__LP64__ + // These two only needed in 32bit builds since NSInteger in 64bit ends up in the LongLong paths. + GTLRPropertyTypeInt32 = 1, + GTLRPropertyTypeUInt32, +#endif + GTLRPropertyTypeLongLong = 3, + GTLRPropertyTypeULongLong, + GTLRPropertyTypeFloat, + GTLRPropertyTypeDouble, + GTLRPropertyTypeBool, + GTLRPropertyTypeNSString, + GTLRPropertyTypeNSNumber, + GTLRPropertyTypeGTLRDateTime, + GTLRPropertyTypeGTLRDuration, + GTLRPropertyTypeNSArray, + GTLRPropertyTypeNSObject, + GTLRPropertyTypeGTLRObject, +}; + +typedef struct { + const char *attributePrefix; + + GTLRPropertyType propertyType; + const char *setterEncoding; + const char *getterEncoding; + + // These are the "fixed" return classes, but some properties will require + // looking up the return class instead (because it is a subclass of + // GTLRObject). + const char *returnClassName; + Class returnClass; + BOOL extractReturnClass; + +} GTLRDynamicImpInfo; + +static const GTLRDynamicImpInfo *DynamicImpInfoForProperty(objc_property_t prop, + Class *outReturnClass) { + + if (outReturnClass) *outReturnClass = nil; + + // dynamic method resolution: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html + // + // property runtimes: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html + + // Get and parse the property attributes, which look something like + // T@"NSString",&,D,P + // Ti,D -- NSInteger on 32bit + // Tq,D -- NSInteger on 64bit, long long on 32bit & 64bit + // TB,D -- BOOL comes as bool on 64bit iOS + // Tc,D -- BOOL comes as char otherwise + // T@"NSString",D + // T@"GTLRLink",D + // T@"NSArray",D + + + static GTLRDynamicImpInfo kImplInfo[] = { +#if !defined(__LP64__) || !__LP64__ + { // NSInteger on 32bit + "Ti", + GTLRPropertyTypeInt32, + "v@:i", + "i@:", + nil, nil, + NO + }, + { // NSUInteger on 32bit + "TI", + GTLRPropertyTypeUInt32, + "v@:I", + "I@:", + nil, nil, + NO + }, +#endif + { // NSInteger on 64bit, long long on 32bit and 64bit. + "Tq", + GTLRPropertyTypeLongLong, + "v@:q", + "q@:", + nil, nil, + NO + }, + { // NSUInteger on 64bit, long long on 32bit and 64bit. + "TQ", + GTLRPropertyTypeULongLong, + "v@:Q", + "Q@:", + nil, nil, + NO + }, + { // float + "Tf", + GTLRPropertyTypeFloat, + "v@:f", + "f@:", + nil, nil, + NO + }, + { // double + "Td", + GTLRPropertyTypeDouble, + "v@:d", + "d@:", + nil, nil, + NO + }, +#if defined(OBJC_BOOL_IS_BOOL) && OBJC_BOOL_IS_BOOL + { // BOOL as bool + "TB", + GTLRPropertyTypeBool, + "v@:B", + "B@:", + nil, nil, + NO + }, +#elif defined(OBJC_BOOL_IS_CHAR) && OBJC_BOOL_IS_CHAR + { // BOOL as char + "Tc", + GTLRPropertyTypeBool, + "v@:c", + "c@:", + nil, nil, + NO + }, +#else + #error unknown definition for ObjC BOOL type +#endif + { // NSString + "T@\"NSString\"", + GTLRPropertyTypeNSString, + "v@:@", + "@@:", + "NSString", nil, + NO + }, + { // NSNumber + "T@\"NSNumber\"", + GTLRPropertyTypeNSNumber, + "v@:@", + "@@:", + "NSNumber", nil, + NO + }, + { // GTLRDateTime + "T@\"" GTLR_CLASSNAME_CSTR(GTLRDateTime) "\"", + GTLRPropertyTypeGTLRDateTime, + "v@:@", + "@@:", + GTLR_CLASSNAME_CSTR(GTLRDateTime), nil, + NO + }, + { // GTLRDuration + "T@\"" GTLR_CLASSNAME_CSTR(GTLRDuration) "\"", + GTLRPropertyTypeGTLRDuration, + "v@:@", + "@@:", + GTLR_CLASSNAME_CSTR(GTLRDuration), nil, + NO + }, + { // NSArray with type + "T@\"NSArray\"", + GTLRPropertyTypeNSArray, + "v@:@", + "@@:", + "NSArray", nil, + NO + }, + { // id (any of the objects above) + "T@,", + GTLRPropertyTypeNSObject, + "v@:@", + "@@:", + "NSObject", nil, + NO + }, + { // GTLRObject - Last, cause it's a special case and prefix is general + "T@\"", + GTLRPropertyTypeGTLRObject, + "v@:@", + "@@:", + nil, nil, + YES + }, + }; + + static BOOL hasLookedUpClasses = NO; + if (!hasLookedUpClasses) { + // Unfortunately, you can't put [NSString class] into the static structure, + // so this lookup has to be done at runtime. + hasLookedUpClasses = YES; + for (uint32_t idx = 0; idx < sizeof(kImplInfo)/sizeof(kImplInfo[0]); ++idx) { + if (kImplInfo[idx].returnClassName) { + kImplInfo[idx].returnClass = objc_getClass(kImplInfo[idx].returnClassName); + NSCAssert1(kImplInfo[idx].returnClass != nil, + @"GTLRRuntimeCommon: class lookup failed: %s", kImplInfo[idx].returnClassName); + } + } + } + + const char *attr = property_getAttributes(prop); + + const char *dynamicMarker = strstr(attr, ",D"); + if (!dynamicMarker || + (dynamicMarker[2] != 0 && dynamicMarker[2] != ',' )) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: property %s isn't dynamic, attributes %s", + property_getName(prop), attr ? attr : "(nil)"); + return NULL; + } + + const GTLRDynamicImpInfo *result = NULL; + + // Cycle over the list + + for (uint32_t idx = 0; idx < sizeof(kImplInfo)/sizeof(kImplInfo[0]); ++idx) { + const char *attributePrefix = kImplInfo[idx].attributePrefix; + if (strncmp(attr, attributePrefix, strlen(attributePrefix)) == 0) { + result = &kImplInfo[idx]; + if (outReturnClass) *outReturnClass = result->returnClass; + break; + } + } + + if (result == NULL) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: unexpected attributes %s for property %s", + attr ? attr : "(nil)", property_getName(prop)); + return NULL; + } + + if (result->extractReturnClass && outReturnClass) { + + // add a null at the next quotation mark + char *attrCopy = strdup(attr); + char *classNameStart = attrCopy + 3; + char *classNameEnd = strstr(classNameStart, "\""); + if (classNameEnd) { + *classNameEnd = '\0'; + + // Lookup the return class + *outReturnClass = objc_getClass(classNameStart); + if (*outReturnClass == nil) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: did not find class with name \"%s\" " + @"for property \"%s\" with attributes \"%s\"", + classNameStart, property_getName(prop), attr); + } + } else { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: Failed to find end of class name for " + @"property \"%s\" with attributes \"%s\"", + property_getName(prop), attr); + } + free(attrCopy); + } + + return result; +} + +// Helper to get the IMP for wiring up the getters. +// NOTE: Every argument passed in should be safe to capture in a block. Avoid +// passing something like selName instead of sel, because nothing says that +// pointer will be valid when it is finally used when the method IMP is invoked +// some time later. +static IMP GTLRRuntimeGetterIMP(SEL sel, + GTLRPropertyType propertyType, + NSString *jsonKey, + Class containedClass, + Class returnClass) { + // Only used in DEBUG logging. +#pragma unused(sel) + + IMP resultIMP; + switch (propertyType) { + +#if !defined(__LP64__) || !__LP64__ + case GTLRPropertyTypeInt32: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + NSInteger result = num.integerValue; + return result; + }); + break; + } + + case GTLRPropertyTypeUInt32: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + NSUInteger result = num.unsignedIntegerValue; + return result; + }); + break; + } +#endif // __LP64__ + + case GTLRPropertyTypeLongLong: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + long long result = num.longLongValue; + return result; + }); + break; + } + + case GTLRPropertyTypeULongLong: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + unsigned long long result = num.unsignedLongLongValue; + return result; + }); + break; + } + + case GTLRPropertyTypeFloat: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + float result = num.floatValue; + return result; + }); + break; + } + + case GTLRPropertyTypeDouble: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + double result = num.doubleValue; + return result; + }); + break; + } + + case GTLRPropertyTypeBool: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + BOOL flag = num.boolValue; + return flag; + }); + break; + } + + case GTLRPropertyTypeNSString: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSString *str = [obj JSONValueForKey:jsonKey]; + return str; + }); + break; + } + + case GTLRPropertyTypeGTLRDateTime: { + resultIMP = imp_implementationWithBlock(^GTLRDateTime *(GTLRObject<GTLRRuntimeCommon> *obj) { + // Return the cached object before creating on demand. + GTLRDateTime *cachedDateTime = [obj cacheChildForKey:jsonKey]; + if (cachedDateTime != nil) { + return cachedDateTime; + } + NSString *str = [obj JSONValueForKey:jsonKey]; + id cacheValue, resultValue; + if (![str isKindOfClass:[NSNull class]]) { + GTLRDateTime *dateTime = [GTLRDateTime dateTimeWithRFC3339String:str]; + + cacheValue = dateTime; + resultValue = dateTime; + } else { + cacheValue = nil; + resultValue = [NSNull null]; + } + [obj setCacheChild:cacheValue forKey:jsonKey]; + return resultValue; + }); + break; + } + + case GTLRPropertyTypeGTLRDuration: { + resultIMP = imp_implementationWithBlock(^GTLRDuration *(GTLRObject<GTLRRuntimeCommon> *obj) { + // Return the cached object before creating on demand. + GTLRDuration *cachedDuration = [obj cacheChildForKey:jsonKey]; + if (cachedDuration != nil) { + return cachedDuration; + } + NSString *str = [obj JSONValueForKey:jsonKey]; + id cacheValue, resultValue; + if (![str isKindOfClass:[NSNull class]]) { + GTLRDuration *duration = [GTLRDuration durationWithJSONString:str]; + + cacheValue = duration; + resultValue = duration; + } else { + cacheValue = nil; + resultValue = [NSNull null]; + } + [obj setCacheChild:cacheValue forKey:jsonKey]; + return resultValue; + }); + break; + } + + case GTLRPropertyTypeNSNumber: { + resultIMP = imp_implementationWithBlock(^(id obj) { + NSNumber *num = [obj JSONValueForKey:jsonKey]; + num = GTLR_EnsureNSNumber(num); + return num; + }); + break; + } + + case GTLRPropertyTypeGTLRObject: { + // Default return class to GTLRObject if it wasn't found. + if (returnClass == Nil) { + returnClass = [GTLRObject class]; + } + resultIMP = imp_implementationWithBlock(^GTLRObject *(GTLRObject<GTLRRuntimeCommon> *obj) { + // Return the cached object before creating on demand. + GTLRObject *cachedObj = [obj cacheChildForKey:jsonKey]; + if (cachedObj != nil) { + return cachedObj; + } + NSMutableDictionary *dict = [obj JSONValueForKey:jsonKey]; + if ([dict isKindOfClass:[NSMutableDictionary class]]) { + id<GTLRObjectClassResolver>objectClassResolver = [obj objectClassResolver]; + GTLRObject *subObj = [GTLRObject objectForJSON:dict + defaultClass:returnClass + objectClassResolver:objectClassResolver]; + [obj setCacheChild:subObj forKey:jsonKey]; + return subObj; + } else if ([dict isKindOfClass:[NSNull class]]) { + [obj setCacheChild:nil forKey:jsonKey]; + return (GTLRObject*)[NSNull null]; + } else if (dict != nil) { + // unexpected; probably got a string -- let the caller figure it out + GTLR_DEBUG_LOG(@"GTLRObject: unexpected JSON: %@.%@ should be a dictionary, actually is a %@:\n%@", + NSStringFromClass([obj class]), + NSStringFromSelector(sel), + NSStringFromClass([dict class]), dict); + return (GTLRObject *)dict; + } + return nil; + }); + break; + } + + case GTLRPropertyTypeNSArray: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj) { + // Return the cached array before creating on demand. + NSMutableArray *cachedArray = [obj cacheChildForKey:jsonKey]; + if (cachedArray != nil) { + return cachedArray; + } + NSMutableArray *result = nil; + NSArray *array = [obj JSONValueForKey:jsonKey]; + if (array != nil) { + if ([array isKindOfClass:[NSArray class]]) { + id<GTLRObjectClassResolver>objectClassResolver = [obj objectClassResolver]; + result = [GTLRRuntimeCommon objectFromJSON:array + defaultClass:containedClass + objectClassResolver:objectClassResolver + isCacheable:NULL]; + } else { +#if DEBUG + if (![array isKindOfClass:[NSNull class]]) { + GTLR_DEBUG_LOG(@"GTLRObject: unexpected JSON: %@.%@ should be an array, actually is a %@:\n%@", + NSStringFromClass([obj class]), + NSStringFromSelector(sel), + NSStringFromClass([array class]), array); + } +#endif + result = (NSMutableArray *)array; + } + } + [obj setCacheChild:result forKey:jsonKey]; + return result; + }); + break; + } + + case GTLRPropertyTypeNSObject: { + resultIMP = imp_implementationWithBlock(^id(GTLRObject<GTLRRuntimeCommon> *obj) { + // Return the cached object before creating on demand. + id cachedObj = [obj cacheChildForKey:jsonKey]; + if (cachedObj != nil) { + return cachedObj; + } + id jsonObj = [obj JSONValueForKey:jsonKey]; + if (jsonObj != nil) { + BOOL shouldCache = NO; + id<GTLRObjectClassResolver>objectClassResolver = [obj objectClassResolver]; + id result = [GTLRRuntimeCommon objectFromJSON:jsonObj + defaultClass:nil + objectClassResolver:objectClassResolver + isCacheable:&shouldCache]; + + [obj setCacheChild:(shouldCache ? result : nil) + forKey:jsonKey]; + return result; + } + return nil; + }); + break; + } + } // switch(propertyType) + + return resultIMP; +} + +// Helper to get the IMP for wiring up the setters. +// NOTE: Every argument passed in should be safe to capture in a block. Avoid +// passing something like selName instead of sel, because nothing says that +// pointer will be valid when it is finally used when the method IMP is invoked +// some time later. +static IMP GTLRRuntimeSetterIMP(SEL sel, + GTLRPropertyType propertyType, + NSString *jsonKey, + Class containedClass, + Class returnClass) { +#pragma unused(sel, returnClass) + IMP resultIMP; + switch (propertyType) { + +#if !defined(__LP64__) || !__LP64__ + case GTLRPropertyTypeInt32: { + resultIMP = imp_implementationWithBlock(^(id obj, NSInteger val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeUInt32: { + resultIMP = imp_implementationWithBlock(^(id obj, NSUInteger val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } +#endif // __LP64__ + + case GTLRPropertyTypeLongLong: { + resultIMP = imp_implementationWithBlock(^(id obj, long long val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeULongLong: { + resultIMP = imp_implementationWithBlock(^(id obj, + unsigned long long val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeFloat: { + resultIMP = imp_implementationWithBlock(^(id obj, float val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeDouble: { + resultIMP = imp_implementationWithBlock(^(id obj, double val) { + [obj setJSONValue:@(val) forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeBool: { + resultIMP = imp_implementationWithBlock(^(id obj, BOOL val) { + NSNumber *numValue = (NSNumber *)(val ? kCFBooleanTrue : kCFBooleanFalse); + [obj setJSONValue:numValue forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeNSString: { + resultIMP = imp_implementationWithBlock(^(id obj, NSString *val) { + NSString *copiedStr = [val copy]; + [obj setJSONValue:copiedStr forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeGTLRDateTime: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj, + GTLRDateTime *val) { + id cacheValue, jsonValue; + if (![val isKindOfClass:[NSNull class]]) { + jsonValue = val.RFC3339String; + cacheValue = val; + } else { + jsonValue = [NSNull null]; + cacheValue = nil; + } + + [obj setJSONValue:jsonValue forKey:jsonKey]; + [obj setCacheChild:cacheValue forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeGTLRDuration: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj, + GTLRDuration *val) { + id cacheValue, jsonValue; + if (![val isKindOfClass:[NSNull class]]) { + jsonValue = val.jsonString; + cacheValue = val; + } else { + jsonValue = [NSNull null]; + cacheValue = nil; + } + + [obj setJSONValue:jsonValue forKey:jsonKey]; + [obj setCacheChild:cacheValue forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeNSNumber: { + resultIMP = imp_implementationWithBlock(^(id obj, NSNumber *val) { + [obj setJSONValue:val forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeGTLRObject: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj, + GTLRObject *val) { + id cacheValue, jsonValue; + if (![val isKindOfClass:[NSNull class]]) { + NSMutableDictionary *dict = [val JSON]; + if (dict == nil && val != nil) { + // adding an empty object; it should have a JSON dictionary so it + // can hold future assignments + val.JSON = [NSMutableDictionary dictionary]; + jsonValue = val.JSON; + } else { + jsonValue = dict; + } + cacheValue = val; + } else { + jsonValue = [NSNull null]; + cacheValue = nil; + } + [obj setJSONValue:jsonValue forKey:jsonKey]; + [obj setCacheChild:cacheValue forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeNSArray: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj, + NSMutableArray *val) { + id json = [GTLRRuntimeCommon jsonFromAPIObject:val + expectedClass:containedClass + isCacheable:NULL]; + [obj setJSONValue:json forKey:jsonKey]; + [obj setCacheChild:val forKey:jsonKey]; + }); + break; + } + + case GTLRPropertyTypeNSObject: { + resultIMP = imp_implementationWithBlock(^(GTLRObject<GTLRRuntimeCommon> *obj, + id val) { + BOOL shouldCache = NO; + id json = [GTLRRuntimeCommon jsonFromAPIObject:val + expectedClass:Nil + isCacheable:&shouldCache]; + [obj setJSONValue:json forKey:jsonKey]; + [obj setCacheChild:(shouldCache ? val : nil) + forKey:jsonKey]; + }); + break; + } + } // switch(propertyType) + + return resultIMP; +} + +#pragma mark Runtime - wiring point + ++ (BOOL)resolveInstanceMethod:(SEL)sel onClass:(Class<GTLRRuntimeCommon>)onClass { + // dynamic method resolution: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html + // + // property runtimes: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html + + const char *selName = sel_getName(sel); + size_t selNameLen = strlen(selName); + char lastChar = selName[selNameLen - 1]; + BOOL isSetter = (lastChar == ':'); + + // look for a declared property matching this selector name exactly + Class<GTLRRuntimeCommon> foundClass = nil; + + objc_property_t prop = PropertyForSel(onClass, sel, isSetter, &foundClass); + if (prop == NULL || foundClass == nil) { + return NO; // No luck, out of here. + } + + Class returnClass = nil; + const GTLRDynamicImpInfo *implInfo = DynamicImpInfoForProperty(prop, + &returnClass); + if (implInfo == NULL) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: unexpected return type class %s for " + @"property \"%s\" of class \"%s\"", + returnClass ? class_getName(returnClass) : "<nil>", + property_getName(prop), + class_getName(onClass)); + return NO; // Failed to find our impl info, out of here. + } + + const char *propName = property_getName(prop); + NSString *propStr = @(propName); + + // replace the property name with the proper JSON key if it's + // special-cased with a map in the found class; otherwise, the property + // name is the JSON key + // NOTE: These caches that are built up could likely be dropped and do this + // lookup on demand from the class tree. Most are checked once when a method + // is first resolved, so eventually become wasted memory. + NSDictionary *keyMap = + [[foundClass ancestorClass] propertyToJSONKeyMapForClass:foundClass]; + NSString *jsonKey = [keyMap objectForKey:propStr]; + if (jsonKey == nil) { + jsonKey = propStr; + } + + // For arrays we need to look up what the contained class is. + Class containedClass = nil; + if (implInfo->propertyType == GTLRPropertyTypeNSArray) { + NSDictionary *classMap = + [[foundClass ancestorClass] arrayPropertyToClassMapForClass:foundClass]; + containedClass = [classMap objectForKey:jsonKey]; + if (containedClass == Nil) { + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: expected array item class for " + @"property \"%s\" of class \"%s\"", + property_getName(prop), class_getName(foundClass)); + } + } + + // Wire in the method. + IMP imp; + const char *encoding; + if (isSetter) { + imp = GTLRRuntimeSetterIMP(sel, implInfo->propertyType, + jsonKey, containedClass, returnClass); + encoding = implInfo->setterEncoding; + } else { + imp = GTLRRuntimeGetterIMP(sel, implInfo->propertyType, + jsonKey, containedClass, returnClass); + encoding = implInfo->getterEncoding; + } + if (class_addMethod(foundClass, sel, imp, encoding)) { + return YES; + } + // Not much we can do if this fails, but leave a breadcumb in the log. + GTLR_DEBUG_LOG(@"GTLRRuntimeCommon: Failed to wire %@ on %@ (encoding: %s).", + NSStringFromSelector(sel), + NSStringFromClass(foundClass), + encoding); + return NO; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRService.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRService.h @@ -0,0 +1,879 @@ +/* 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. + */ + +// Service object documentation: +// https://github.com/google/google-api-objectivec-client-for-rest/wiki#services-and-tickets + +#import <Foundation/Foundation.h> + +#import "GTLRDefines.h" +#import "GTLRBatchQuery.h" +#import "GTLRBatchResult.h" +#import "GTLRDateTime.h" +#import "GTLRDuration.h" +#import "GTLRErrorObject.h" +#import "GTLRObject.h" +#import "GTLRQuery.h" + +@class GTMSessionFetcher; +@class GTMSessionFetcherService; +@protocol GTMFetcherAuthorizationProtocol; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The domain used used for NSErrors created by GTLRService query execution. + */ +extern NSString *const kGTLRServiceErrorDomain; + +typedef NS_ENUM(NSInteger, GTLRServiceError) { + GTLRServiceErrorQueryResultMissing = -3000, + GTLRServiceErrorBatchResponseUnexpected = -3001, + GTLRServiceErrorBatchResponseStatusCode = -3002 +}; + +/** + * The kGTLRServiceErrorDomain userInfo key for the server response body. + */ +extern NSString *const kGTLRServiceErrorBodyDataKey; + +/** + * The kGTLRServiceErrorDomain userInfo key for the response content ID, if appropriate. + */ +extern NSString *const kGTLRServiceErrorContentIDKey; + +/** + * The domain used for foundation errors created from GTLRErrorObjects that + * were not originally foundation errors. + */ +extern NSString *const kGTLRErrorObjectDomain; + +/** + * The userInfo key for a GTLRErrorObject for errors with domain kGTLRErrorObjectDomain + * when the error was created from a structured JSON error response body. + */ +extern NSString *const kGTLRStructuredErrorKey; + +/** + * A constant ETag for when updating or deleting a single entry, telling + * the server to replace the current value unconditionally. + * + * Do not use this in entries in a batch feed. + */ +extern NSString *const kGTLRETagWildcard; + +/** + * Notification of a ticket starting. The notification object is the ticket. + * This is posted on the main thread. + * + * Use the stopped notification to log all requests made by the library. + */ +extern NSString *const kGTLRServiceTicketStartedNotification; + +/** + * Notification of a ticket stopping. The notification object is the ticket. + * This is posted on the main thread. + */ +extern NSString *const kGTLRServiceTicketStoppedNotification; + +/** + * Notifications when parsing of a server response or entry begins. + * This is posted on the main thread. + */ +extern NSString *const kGTLRServiceTicketParsingStartedNotification; + +/** + * Notifications when parsing of a server response or entry ends. + * This is posted on the main thread. + */ +extern NSString *const kGTLRServiceTicketParsingStoppedNotification; + +/** + * The header name used to send an Application's Bundle Identifier. + * For more information on adding API restrictions see the docs: + * https://cloud.google.com/docs/authentication/api-keys#api_key_restrictions + */ +extern NSString *const kXIosBundleIdHeader; + +@class GTLRServiceTicket; + +/** + * Callback block for query execution. + * + * @param callbackTicket The ticket that tracked query execution. + * @param object The result of query execution. This will be derived from + * GTLRObject. The object may be nil for operations such as DELETE which + * do not return an object. The object will be a GTLRBatchResult for + * batch operations, and GTLRDataObject for media downloads. + * @param callbackError If non-nil, the query execution failed. For batch requests, + * this may be nil even if individual queries in the batch have failed. + */ +typedef void (^GTLRServiceCompletionHandler)(GTLRServiceTicket *callbackTicket, + id _Nullable object, + NSError * _Nullable callbackError); + +/** + * Callback block for upload of query data. + * + * @param progressTicket The ticket that tracks query execution. + * @param totalBytesUploaded Number of bytes uploaded so far. + * @param totalBytesExpectedToUpload Number of bytes expected to be uploaded. + */ +typedef void (^GTLRServiceUploadProgressBlock)(GTLRServiceTicket *progressTicket, + unsigned long long totalBytesUploaded, + unsigned long long totalBytesExpectedToUpload); + +/** + * Callback block invoked when an eror occurs during query execution. + * + * @param retryTicket The ticket that tracks query execution. + * @param suggestedWillRetry Flag indicating if the library would retry this without a retry block. + * @param fetchError The error that occurred. If the domain is + * kGTMSessionFetcherStatusDomain then the error's code is the server + * response status. Details on the error from the server are available + * in the userInfo via the keys kGTLRStructuredErrorKey and + * NSLocalizedDescriptionKey. + * + * @return YES if the request should be retried. + */ +typedef BOOL (^GTLRServiceRetryBlock)(GTLRServiceTicket *retryTicket, + BOOL suggestedWillRetry, + NSError * _Nullable fetchError); + +/** + * Block to be invoked by a test block. + * + * @param object The faked object, if any, to be passed to the test code's completion handler. + * @param error The faked error if any, to be passed to the test code's completion handler. + */ +typedef void (^GTLRServiceTestResponse)(id _Nullable object, NSError *_Nullable error); + +/** + * A test block enables testing of query execution without any network activity. + * + * The test block must finish by calling the response block, passing either an object + * (GTLRObject or GTLRBatchResult) or an NSError. + * + * The query is available to the test block code as testTicket.originalQuery. + * + * Because query execution is asynchronous, the test code must wait for a callback, + * either with GTLRService's waitForTicket:timeout:fetchedObject:error: or with + * XCTestCase's waitForExpectationsWithTimeout: + * + * Example usage is available in GTLRServiceTest. + * + * @param testTicket The ticket that tracks query execution. + * @param testResponse A block that must be invoked by the test block. This may be invoked + * synchronously or asynchornously. + */ +typedef void (^GTLRServiceTestBlock)(GTLRServiceTicket *testTicket, + GTLRServiceTestResponse testResponse); + +#pragma mark - + +/** + * Base class for the service that executes queries and manages tickets. + * + * Client apps will typically use a generated subclass of GTLRService. + */ +@interface GTLRService : NSObject + +#pragma mark Query Execution + +/** + * Executes the supplied query + * + * Success is indicated in the completion handler by a nil error parameter, not by a non-nil + * object parameter. + * + * The callback block is invoked exactly once unless the ticket is cancelled. + * The callback will be called on the service's callback queue. + * + * Various execution parameters will be taken from the service's properties, unless overridden + * in the query's @c executionParameters property. + * + * A query may only be executed a single time. To reuse a query, make a copy before executing + * it. + * + * To get a NSURLRequest that represents the query, use @c -[GTLRService requestForQuery:] + * + * @param query The API query, either a subclass of GTLRQuery, or a GTLRBatchQuery. + * @param handler The execution callback block. + * + * @return A ticket for tracking or canceling query execution. + */ +- (GTLRServiceTicket *)executeQuery:(id<GTLRQueryProtocol>)query + completionHandler:(nullable GTLRServiceCompletionHandler)handler; + +/** + * Executes the supplied query + * + * The callback is invoked exactly once unless the ticket is cancelled. + * The callback will be called on the service's callbackQueue. + * Various execution parameters will be taken from the service's properties, unless overridden + * in the query's @c executionParameters property. + * + * The selector should have a signature matching: + * @code + * - (void)serviceTicket:(GTLRServiceTicket *)callbackTicket + * finishedWithObject:(GTLRObject *)object + * error:(NSError *)callbackError + * @endcode + * + * @param query The API query, either a subclass of GTLRQuery, or a GTLRBatchQuery. + * @param delegate The object to be with the selector to be invoked upon completion. + * @param finishedSelector The selector to be invoked upon completion. + * + * @return A ticket for tracking or canceling query execution. + */ +- (GTLRServiceTicket *)executeQuery:(id<GTLRQueryProtocol>)query + delegate:(nullable id)delegate + didFinishSelector:(nullable SEL)finishedSelector; + + +/** + * Enable automatic pagination. + * + * A ticket can optionally do a sequence of fetches for queries where repeated requests + * with a @c nextPageToken query parameter is required to retrieve all pages of + * the response collection. The client's callback is invoked only when all items have + * been retrieved, or an error has occurred. + * + * The final object may be a combination of multiple page responses + * so it may not be the same as if all results had been returned in a single + * page. Some fields of the response may reflect only the final page's values. + * + * Automatic page fetches will return an error if more than 25 page fetches are + * required. For debug builds, this will log a warning to the console when more + * than 2 page fetches occur, as a reminder that the query's @c maxResults parameter + * should probably be increased to specify more items returned per page. + * + * Automatic page accumulation is available for query result objects that are derived + * from GTLRCollectionObject. + * + * This may also be specified for a single query in the query's @c executionParameters property. + * + * Default value is NO. + */ +@property(nonatomic, assign) BOOL shouldFetchNextPages; + +/** + * Some services require a developer key for quotas and limits. + * + * If you have enabled the iOS API Key Restriction, you will want + * to manually set the @c APIKeyRestrictionBundleID property, or + * use -setMainBundleIDRestrictionWithAPIKey: to set your API key + * and set the restriction to the main bundle's bundle id. + */ +@property(nonatomic, copy, nullable) NSString *APIKey; + +/** + * The Bundle Identifier to use for the API key restriction. This will be + * sent in an X-Ios-Bundle-Identifier header; for more information see + * the API key documentation + * https://cloud.google.com/docs/authentication/api-keys#api_key_restrictions + */ +@property(nonatomic, copy, nullable) NSString *APIKeyRestrictionBundleID; + +/** + * Helper method to set the @c APIKey to the given value and set the + * @c APIKeyRestrictionBundleID to the main bundle's bundle identifier. + */ +- (void)setMainBundleIDRestrictionWithAPIKey:(NSString *)apiKey; + +/** + * An authorizer adds user authentication headers to the request as needed. + * + * This may be overridden on individual queries with the @c shouldSkipAuthorization property. + */ +@property(nonatomic, retain, nullable) id <GTMFetcherAuthorizationProtocol> authorizer; + +/** + * Enable fetcher retry support. See the explanation of retry support in @c GTMSessionFetcher.h + * + * Default value is NO, but retry is also enabled if the retryBlock is not nil. + * + * This may also be specified for a single query in the query's @c executionParameters property. + */ +@property(nonatomic, assign, getter=isRetryEnabled) BOOL retryEnabled; + +/** + * A retry block may be provided to inspect and change retry criteria. + * + * This may also be specified for a single query in the query's @c executionParameters property. + */ +@property(atomic, copy, nullable) GTLRServiceRetryBlock retryBlock; + +/** + * The maximum retry interval. Retries occur at increasing intervals, up to the specified maximum. + * + * This may also be specified for a single query in the query's @c executionParameters property. + */ +@property(nonatomic, assign) NSTimeInterval maxRetryInterval; + +#pragma mark Fetch Object by Resource URL + +/** + * Fetch an object given the resource URL. This is appropriate when the object's + * full link is known, such as from a selfLink response property. + * + * @param resourceURL The URL of the object to be fetched. + * @param objectClass The GTLRObject subclass to be instantiated. If nil, the library + * will try to infer the class from the object's "kind" string property. + * @param executionParameters Values to override the service's properties when executing the + * ticket. + * @param handler The execution callback block. + * + * @return A ticket for tracking or canceling query execution. + */ +- (GTLRServiceTicket *)fetchObjectWithURL:(NSURL *)resourceURL + objectClass:(nullable Class)objectClass + executionParameters:(nullable GTLRServiceExecutionParameters *)executionParameters + completionHandler:(nullable GTLRServiceCompletionHandler)handler; + +#pragma mark Support for Client Tests + +/** + * A test block can be provided to test service calls without any network activity. + * + * See the description of @c GTLRServiceTestBlock for additional details. + * + * This may also be specified for a single query in the query's @c executionParameters property. + * + * A service instance for testing can also be created with @c +mockServiceWithFakedObject + */ +@property(nonatomic, copy, nullable) GTLRServiceTestBlock testBlock; + +#pragma mark Converting a Query to an NSURLRequest + +/** + * Creates a NSURLRequest from the query object and from properties on this service + * (additionalHTTPHeaders, additionalURLQueryParameters, APIKey) without executing + * it. This can be useful for using @c GTMSessionFetcher or @c NSURLSession to + * perform the fetch. + * + * For requests to non-public resources, the request will not yet be authorized; + * that can be done using the GTLR service's authorizer. Creating a @c GTMSessionFetcher + * from the GTLRService's @c fetcherService will take care of authorization as well. + * + * This works only for GET queries, and only for an individual query, not a batch query. + * + * @note @c Unlike executeQuery:, requestForQuery: does not release the query's callback blocks. + * + * @param query The query used to create the request. + * + * @return A request suitable for use with @c GTMSessionFetcher or @c NSURLSession + */ +- (NSMutableURLRequest *)requestForQuery:(GTLRQuery *)query; + +#pragma mark User Properties + +/** + * The service properties dictionary is copied to become the initial property dictionary + * for each ticket, augmented by a query's execution parameter's properties. + */ +@property(nonatomic, copy, nullable) NSDictionary<NSString *, id> *serviceProperties; + +#pragma mark JSON to GTLRObject Mapping + +/** + * Specifies subclasses to be created instead of standard library objects, allowing + * an app to add properties and methods to GTLR objects. + * + * This is just a helper method that sets the service's objectClassResolver:. + * + * Example: + * @code + * NSDictionary *surrogates = @{ + * [MyDriveFile class] : [GTLRDrive_File_Surrogate class], + * [MyDriveFileList class] : [GTLRDrive_FileList_Surrogate class] + * }; + * [service setSurrogates:surrogates]; + * @endcode + */ +- (void)setSurrogates:(NSDictionary <Class, Class>*)surrogates; + +/** + * Used to decide what GTLRObject subclass to make from the received JSON. + * + * This defaults to a resolver that will use any kindStringToClassMap the service + * provides. + * + * To use a standard resolver with a surrogates dictionary, invoke setSurrogates: instead + * of setting this property. + */ +@property(nonatomic, strong) id<GTLRObjectClassResolver> objectClassResolver; + +/** + * A dictionary mapping "kind" strings to the GTLObject subclasses that should + * be created for JSON with the given kind. + */ ++ (NSDictionary<NSString *, Class> *)kindStringToClassMap; + +#pragma mark Request Settings + +/** + * The queue used to invoked callbacks. By default, the main queue is used for callbacks. + */ +@property(nonatomic, retain) dispatch_queue_t callbackQueue; + +/** + * Allows the application to make non-SSL and localhost requests for testing. + * + * Default value is NO. + */ +@property(nonatomic, assign) BOOL allowInsecureQueries; + +/** + * The fetcher service creates the fetcher instances for this API service. + * + * Applications may set this to an authorized fetcher service created elsewhere + * in the app, or may take the fetcher service created by this GTLRService and use it + * to create fetchers independent of this service. + */ +@property(nonatomic, retain) GTMSessionFetcherService *fetcherService; + +#pragma mark Custom User Agents + +/** + * Applications needing an additional identifier in the server logs may specify one + * through this property and it will be added to the existing UserAgent. It should + * already be a valid identifier as no cleaning/validation is done. + */ +@property(nonatomic, copy, nullable) NSString *userAgentAddition; + +/** + * A base user-agent based on the application signature in the Info.plist settings. + * + * Most applications should not explicitly set this property. Any string provided will + * be cleaned of inappropriate characters. + */ +@property(nonatomic, copy, nullable) NSString *userAgent; + +/** + * The request user agent includes the library and OS version appended to the + * base userAgent, along with the optional addition string. + */ +@property(nonatomic, readonly, nullable) NSString *requestUserAgent; + +/** + * A precise base userAgent string identifying the application. No cleaning of characters + * is done. Library-specific details will be appended. + * + * @param userAgent A wire-ready user agent string. + */ +- (void)setExactUserAgent:(nullable NSString *)userAgent; + +/** + * A precise userAgent string to send on requests; no cleaning is done. When + * set, requestUserAgent will be exactly this, no library or system information + * will be auto added. + * + * @param requestUserAgent A wire-ready user agent string. + */ +- (void)overrideRequestUserAgent:(nullable NSString *)requestUserAgent; + +/** + * Any additional URL query parameters for the queries executed by this service. + * + * Individual queries may have additionalURLQueryParameters specified as well. + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalURLQueryParameters; + +/** + * Any additional HTTP headers for this queries executed by this service. + * + * Individual queries may have additionalHTTPHeaders specified as well. + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, NSString *> *additionalHTTPHeaders; + +#pragma mark Request URL Construction + +/* + * The URL for where to send a Query is built out of these parts + * ( https://developers.google.com/discovery/v1/using#build-compose ) : + * + * service.rootURLString + service.servicePath + query.pathURITemplate + * + * Note: odds are these both should end in a '/', so make sure any value you + * provide will combine correctly with the above rules. + */ + +/** + * The scheme and host for the API server. This may be modified to point at a test server. + */ +@property(nonatomic, copy) NSString *rootURLString; + +/** + * The path for the specific API service instance, relative to the rootURLString. + */ +@property(nonatomic, copy) NSString *servicePath; + +/** + * A path fragment added in to URLs before "servicePath" to build + * the full URL used for resumable media uploads. + */ +@property(nonatomic, copy) NSString *resumableUploadPath; + +/** + * A path fragment added in to URLs before "servicePath" to build + * the full URL used for simple and multipart media uploads. + */ +@property(nonatomic, copy) NSString *simpleUploadPath; + +/** + * A path fragment added in to URLs before "servicePath" to build + * the full URL used for batch requests. + */ +@property(nonatomic, copy) NSString *batchPath; + +#pragma mark Resumable Uploads + +/** + * A block called to track upload progress. + * + * A query's service execution parameters may be used to override this. + */ +@property(nonatomic, copy, nullable) GTLRServiceUploadProgressBlock uploadProgressBlock; + +/** + * The default chunk size for resumable uploads. This defaults to kGTLRStandardUploadChunkSize + * for service subclasses that support chunked uploads. + */ +@property(nonatomic, assign) NSUInteger serviceUploadChunkSize; + +/** + * Service subclasses may override this to specify their own default chunk size for + * resumable uploads. + */ ++ (NSUInteger)defaultServiceUploadChunkSize; + +#pragma mark Internal +///////////////////////////////////////////////////////////////////////////////////////////// +// +// Properties below are used by the library and should not typically be set by client apps. +// +///////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The queue used for parsing JSON responses. + * + * Applications should typically not change this. + */ +@property(nonatomic, retain) dispatch_queue_t parseQueue; + +/** + * If this service supports pretty printing the JSON on the wire, these are + * the names of the query params that enable it. If there are any values, + * the library disables pretty printing to save on bandwidth. + * + * Applications should typically not need change this; the ServiceGenerator + * will set this up when generating the custom subclass. + */ +@property(nonatomic, strong, nullable) NSArray<NSString *> *prettyPrintQueryParameterNames; + +/** + * This indicates if the API requires a "data" JSON element to wrap the payload + * on requests and responses. + * + * Applications should typically not change this. + */ +@property(nonatomic, assign, getter=isDataWrapperRequired) BOOL dataWrapperRequired; + +@end + +@interface GTLRService (TestingSupport) + +/** + * Convenience method to create a mock GTLR service just for testing. + * + * Queries executed by this mock service will not perform any network operation, + * but will invoke callbacks and provide the supplied object or error to the + * completion handler. + * + * You can make more customized mocks by setting the test block property of a service + * or a query's execution parameters. The test block can inspect the query as ticket.originalQuery + * to customize test behavior. + * + * See the description of @c GTLRServiceTestBlock for more details on customized testing. + * + * Example usage is in the unit test method @c testService_MockService_Succeeding + * + * @param object An object derived from GTLRObject to be passed to query completion handlers. + * @param error An error to be passed to query completion handlers. + * + * @return A mock instance of the service, suitable for unit testing. + */ ++ (instancetype)mockServiceWithFakedObject:(nullable id)object + fakedError:(nullable NSError *)error; + +/** + * Wait synchronously for fetch to complete (strongly discouraged) + * + * This method is intended for use only in unit tests and command-line tools. + * Unit tests may also use XCTest's waitForExpectationsWithTimeout: instead of + * or after this method. + * + * This method just runs the current event loop until the fetch completes + * or the timout limit is reached. This may discard unexpected events + * that occur while spinning, so it's really not appropriate for use + * in serious applications. + * + * Returns YES if an object was successfully fetched. If the wait + * timed out, returns NO and the returned error is nil. + * + * @param ticket The ticket being executed. + * @param timeoutInSeconds Maximum duration to wait. + * + * @return YES if the ticket completed or was cancelled; NO if the wait timed out. + */ +- (BOOL)waitForTicket:(GTLRServiceTicket *)ticket + timeout:(NSTimeInterval)timeoutInSeconds; + +@end + +#pragma mark - + +/** + * Service execution parameters may be set on an individual query + * to alter the service's settings. + */ +@interface GTLRServiceExecutionParameters : NSObject<NSCopying> + +/** + * Override the service's property @c shouldFetchNextPages for automatic pagination. + * + * A BOOL value should be specified. + */ +@property(atomic, strong, nullable) NSNumber *shouldFetchNextPages; + +/** + * Override the service's property @c shouldFetchNextPages for enabling automatic retries. + * + * A BOOL value should be specified. + * + * Retry is also enabled if the retryBlock is not nil + */ +@property(atomic, strong, nullable, getter=isRetryEnabled) NSNumber *retryEnabled; + +/** + * Override the service's property @c retryBlock for customizing automatic retries. + */ +@property(atomic, copy, nullable) GTLRServiceRetryBlock retryBlock; + +/** + * Override the service's property @c maxRetryInterval for customizing automatic retries. + * + * A NSTimeInterval (double) value should be specified. + */ +@property(atomic, strong, nullable) NSNumber *maxRetryInterval; + +/** + * Override the service's property @c uploadProgressBlock for monitoring upload progress. + */ +@property(atomic, copy, nullable) GTLRServiceUploadProgressBlock uploadProgressBlock; + +/** + * Override the service's property @c callbackQueue for invoking callbacks. + */ +@property(atomic, retain, nullable) dispatch_queue_t callbackQueue; + +/** + * Override the service's property @c testBlock for simulating query execution. + * + * See the description of @c GTLRServiceTestBlock for additional details. + */ +@property(atomic, copy, nullable) GTLRServiceTestBlock testBlock; + +/** + * Override the service's property @c objectClassResolver for controlling object class selection. + */ +@property(atomic, strong, nullable) id<GTLRObjectClassResolver> objectClassResolver; + +/** + * The ticket's properties are the service properties, with the execution parameter's + * ticketProperties added (replacing any keys already present from the service.) + */ +@property(atomic, copy, nullable) NSDictionary<NSString *, id> *ticketProperties; + +/** + * Indicates if any of the execution parameters properties are set. + */ +@property(nonatomic, readonly) BOOL hasParameters; + +@end + +/** + * A ticket tracks the progress of a query being executed. + */ +@interface GTLRServiceTicket : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + * The service that issued this ticket. + * + * This method may be invoked from any thread. + */ +@property(atomic, readonly) GTLRService *service; + +#pragma mark Execution Control + +/** + * Invoking cancelTicket stops the fetch if it is in progress. The query callbacks + * will not be invoked. + * + * This method may be invoked from any thread. + */ +- (void)cancelTicket; + +/** + * The time the ticket was created. + */ +@property(atomic, readonly) NSDate *creationDate; + +/** + * Pause the ticket execution. This is valid only for chunked, resumable upload queries. + */ +- (void)pauseUpload; + +/** + * Resume the ticket execution. This is valid only for chunked, resumable upload queries. + */ +- (void)resumeUpload; + +/** + * Checks if the ticket execution is paused. + */ +@property(nonatomic, readonly, getter=isUploadPaused) BOOL uploadPaused; + +/** + * The request being fetched for the query. + */ +@property(nonatomic, readonly, nullable) NSURLRequest *fetchRequest; + +/** + * The fetcher being used for the query request. + */ +@property(atomic, readonly, nullable) GTMSessionFetcher *objectFetcher; + +/** + * The queue used for query callbacks. + */ +@property(atomic, readonly) dispatch_queue_t callbackQueue; + +/** + * The API key used for the query requeat. + */ +@property(atomic, readonly, nullable) NSString *APIKey; + +/** + * The Bundle Identifier to use for the API key restriciton. + */ +@property(atomic, readonly, nullable) NSString *APIKeyRestrictionBundleID; + +#pragma mark Status + +/** + * The server's response status for the query's fetch, if available. + */ +@property(nonatomic, readonly) NSInteger statusCode; + +/** + * The error resulting from the query's fetch, if available. + */ +@property(nonatomic, readonly, nullable) NSError *fetchError; + +/** + * A flag indicating if the query's callbacks have been invoked. + */ +@property(nonatomic, readonly) BOOL hasCalledCallback; + +/** + * A flag indicating if the query execution was cancelled by the client app. + */ +@property(atomic, readonly, getter=isCancelled) BOOL cancelled; + +#pragma mark Pagination + +/** + * A flag indicating if automatic pagination is enabled for the query. + */ +@property(nonatomic, readonly) BOOL shouldFetchNextPages; + +/** + * The number of pages fetched, if automatic pagination is enabled for the query and multiple + * pages have been fetched. + */ +@property(nonatomic, readonly) NSUInteger pagesFetchedCounter; + +#pragma mark User Properties + +/** + * Ticket properties a way to pass values via the ticket for the convenience of the client app. + * + * Ticket properties are initialized from serviceProperties and augmented by the ticketProperties + * of the query's execution parameters. + */ +@property(nonatomic, readonly, nullable) NSDictionary<NSString *, id> *ticketProperties; + +#pragma mark Payload + +/** + * The object being uploaded via POST, PUT, or PATCH. + */ +@property(nonatomic, readonly, nullable) GTLRObject *postedObject; + +/** + * The object downloaded for the query, after parsing. + */ +@property(nonatomic, readonly, nullable) GTLRObject *fetchedObject; + +/** + * The query currently being fetched by this ticket. This may not be the original query when + * fetching a second or later pages. + */ +@property(atomic, readonly, nullable) id<GTLRQueryProtocol> executingQuery; + +/** + * The query used to create this ticket + */ +@property(atomic, readonly, nullable) id<GTLRQueryProtocol> originalQuery; + +/** + * The @c GTLRObjectClassResolver for controlling object class selection. + */ +@property(atomic, readonly, strong) id<GTLRObjectClassResolver> objectClassResolver; + +/** + * The query from within the ticket's batch request with the given ID. + * + * @param requestID The desired ticket's request ID. + * + * @return The query with the specified ID, if found. + */ +- (nullable GTLRQuery *)queryForRequestID:(NSString *)requestID; + +@end + +/** + * The library doesn't use GTLRObjectCollectionImpl, but it provides a concrete implementation + * so the methods do not cause private method errors in Xcode/AppStore review. + */ +@interface GTLRObjectCollectionImpl : GTLRObject +@property(nonatomic, copy) NSString *nextPageToken; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRService.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRService.m @@ -0,0 +1,2883 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import <TargetConditionals.h> + +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif + +#if !defined(GTLR_USE_FRAMEWORK_IMPORTS) + #if defined(COCOAPODS) && COCOAPODS + #define GTLR_USE_FRAMEWORK_IMPORTS 1 + #else + #define GTLR_USE_FRAMEWORK_IMPORTS 0 + #endif +#endif + +#import "GTLRService.h" + +#import "GTLRFramework.h" +#import "GTLRURITemplate.h" +#import "GTLRUtilities.h" + +#if GTLR_USE_FRAMEWORK_IMPORTS + #import <GTMSessionFetcher/GTMSessionFetcher.h> + #import <GTMSessionFetcher/GTMSessionFetcherService.h> + #import <GTMSessionFetcher/GTMMIMEDocument.h> +#else + #import "GTMSessionFetcher.h" + #import "GTMSessionFetcherService.h" + #import "GTMMIMEDocument.h" +#endif // GTLR_USE_FRAMEWORK_IMPORTS + + +#ifndef STRIP_GTM_FETCH_LOGGING + #error GTMSessionFetcher headers should have defaulted this if it wasn't already defined. +#endif + +NSString *const kGTLRServiceErrorDomain = @"com.google.GTLRServiceDomain"; +NSString *const kGTLRErrorObjectDomain = @"com.google.GTLRErrorObjectDomain"; +NSString *const kGTLRServiceErrorBodyDataKey = @"body"; +NSString *const kGTLRServiceErrorContentIDKey = @"contentID"; +NSString *const kGTLRStructuredErrorKey = @"GTLRStructuredError"; +NSString *const kGTLRETagWildcard = @"*"; + +NSString *const kGTLRServiceTicketStartedNotification = @"kGTLRServiceTicketStartedNotification"; +NSString *const kGTLRServiceTicketStoppedNotification = @"kGTLRServiceTicketStoppedNotification"; +NSString *const kGTLRServiceTicketParsingStartedNotification = @"kGTLRServiceTicketParsingStartedNotification"; +NSString *const kGTLRServiceTicketParsingStoppedNotification = @"kGTLRServiceTicketParsingStoppedNotification"; + +NSString *const kXIosBundleIdHeader = @"X-Ios-Bundle-Identifier"; + +static NSString *const kDeveloperAPIQueryParamKey = @"key"; + +static const NSUInteger kMaxNumberOfNextPagesFetched = 25; + +static const NSUInteger kMaxGETURLLength = 2048; + +// we'll enforce 50K chunks minimum just to avoid the server getting hit +// with too many small upload chunks +static const NSUInteger kMinimumUploadChunkSize = 50000; + +// Helper to get the ETag if it is defined on an object. +static NSString *ETagIfPresent(GTLRObject *obj) { + NSString *result = [obj.JSON objectForKey:@"etag"]; + return result; +} + +// Merge two dictionaries. Either may be nil. +// If both are nil, return nil. +// In case of a key collision, values of the second dictionary prevail. +static NSDictionary *MergeDictionaries(NSDictionary *recessiveDict, NSDictionary *dominantDict) { + if (!dominantDict) return recessiveDict; + if (!recessiveDict) return dominantDict; + + NSMutableDictionary *worker = [recessiveDict mutableCopy]; + [worker addEntriesFromDictionary:dominantDict]; + return worker; +} + +@interface GTLRServiceTicket () + +- (instancetype)initWithService:(GTLRService *)service + executionParameters:(GTLRServiceExecutionParameters *)params NS_DESIGNATED_INITIALIZER; + +// Thread safety: ticket properties are all publicly exposed as read-only. +// +// Service execution of a ticket is serial (started by the app, then executing on the fetcher +// callback queue and then the parse queue), so we don't need to worry about synchronization. +// +// One important exception is when the user invoked cancelTicket. During cancellation, ticket +// properties are released. This should be harmless even during the fetch start-parse-callback +// phase because nothing released in cancelTicket is used to begin a fetch, and the cancellation +// flag will prevent any application callbacks from being invoked. +// +// The cancel and objectFetcher properties are synchronized on the ticket. + +// Ticket properties exposed publicly as readonly. +@property(atomic, readwrite, nullable) id<GTLRQueryProtocol> originalQuery; +@property(atomic, readwrite, nullable) id<GTLRQueryProtocol> executingQuery; +@property(atomic, readwrite, nullable) GTMSessionFetcher *objectFetcher; +@property(nonatomic, readwrite, nullable) NSURLRequest *fetchRequest; +@property(nonatomic, readwrite, nullable) GTLRObject *postedObject; +@property(nonatomic, readwrite, nullable) GTLRObject *fetchedObject; +@property(nonatomic, readwrite, nullable) NSError *fetchError; +@property(nonatomic, readwrite) BOOL hasCalledCallback; +@property(nonatomic, readwrite) NSUInteger pagesFetchedCounter; +@property(readwrite, atomic, strong) id<GTLRObjectClassResolver> objectClassResolver; + +// Internal properties copied from the service. +@property(nonatomic, assign) BOOL allowInsecureQueries; +@property(nonatomic, strong) GTMSessionFetcherService *fetcherService; +@property(nonatomic, strong, nullable) id<GTMFetcherAuthorizationProtocol> authorizer; + +// Internal properties copied from serviceExecutionParameters. +@property(nonatomic, getter=isRetryEnabled) BOOL retryEnabled; +@property(nonatomic, readwrite) NSTimeInterval maxRetryInterval; +@property(nonatomic, strong, nullable) GTLRServiceRetryBlock retryBlock; +@property(nonatomic, strong, nullable) GTLRServiceUploadProgressBlock uploadProgressBlock; +@property(nonatomic, strong, nullable) GTLRServiceTestBlock testBlock; +@property(nonatomic, readwrite) BOOL shouldFetchNextPages; + +// Internal properties used by the service. +#if GTM_BACKGROUND_TASK_FETCHING +// Access to backgroundTaskIdentifier should be protected by @synchronized(self). +@property(nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier; +#endif // GTM_BACKGROUND_TASK_FETCHING + +// Dispatch group enabling waitForTicket: to delay until async callbacks and notifications +// related to the ticket have completed. +@property(nonatomic, readonly) dispatch_group_t callbackGroup; + +// startBackgroundTask and endBackgroundTask do nothing if !GTM_BACKGROUND_TASK_FETCHING +- (void)startBackgroundTask; +- (void)endBackgroundTask; + +- (void)notifyStarting:(BOOL)isStarting; +- (void)releaseTicketCallbacks; + +// Posts a notification on the main queue using the ticket's dispatch group. +- (void)postNotificationOnMainThreadWithName:(NSString *)name + object:(id)object + userInfo:(NSDictionary *)userInfo; +@end + +#if !defined(GTLR_HAS_SESSION_UPLOAD_FETCHER_IMPORT) + #if defined(COCOAPODS) && COCOAPODS + #define GTLR_HAS_SESSION_UPLOAD_FETCHER_IMPORT 1 + #else + #define GTLR_HAS_SESSION_UPLOAD_FETCHER_IMPORT 0 + #endif +#endif + +#if GTLR_HAS_SESSION_UPLOAD_FETCHER_IMPORT + #if GTLR_USE_FRAMEWORK_IMPORTS + #import <GTMSessionFetcher/GTMSessionUploadFetcher.h> + #else + #import "GTMSessionUploadFetcher.h" + #endif // GTLR_USE_FRAMEWORK_IMPORTS +#else +// If the upload fetcher class is available, it can be used for chunked uploads +// +// We locally declare some methods of the upload fetcher so we +// do not need to import the header, as some projects may not have it available +@interface GTMSessionUploadFetcher : GTMSessionFetcher + ++ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil; + ++ (instancetype)uploadFetcherWithLocation:(NSURL *)uploadLocationURL + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(int64_t)chunkSize + fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil; + +@property(strong) NSURL *uploadLocationURL; +@property(strong) NSData *uploadData; +@property(strong) NSURL *uploadFileURL; +@property(strong) NSFileHandle *uploadFileHandle; + +- (void)pauseFetching; +- (void)resumeFetching; +- (BOOL)isPaused; +@end +#endif // GTLR_HAS_SESSION_UPLOAD_FETCHER_IMPORT + + +@interface GTLRObject (StandardProperties) +// Common properties on GTLRObject that are invoked below. +@property(nonatomic, copy) NSString *nextPageToken; +@end + +// This class encapsulates the pieces of a single batch response, including +// inner http response code and message, inner headers, JSON body (parsed as a dictionary), +// or parsing NSError. +// +// See responsePartsWithMIMEParts: for an example of the wire format data used +// to populate this object. +@interface GTLRBatchResponsePart : NSObject +@property(nonatomic, copy) NSString *contentID; +@property(nonatomic, assign) NSInteger statusCode; +@property(nonatomic, copy) NSString *statusString; +@property(nonatomic, strong) NSDictionary *headers; +@property(nonatomic, strong) NSDictionary *JSON; +@property(nonatomic, strong) NSError *parseError; +@end + +@implementation GTLRBatchResponsePart +@synthesize contentID = _contentID, + headers = _headers, + JSON = _JSON, + parseError = _parseError, + statusCode = _statusCode, + statusString = _statusString; +#if DEBUG +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p: %@\n%ld %@\nheaders:%@\nJSON:%@\nerror:%@", + [self class], self, self.contentID, (long)self.statusCode, self.statusString, + self.headers, self.JSON, self.parseError]; +} +#endif +@end + +// GTLRResourceURLQuery is an internal class used as a query object placeholder +// when fetchObjectWithURL: is invoked by the client app. This lets the service's +// plumbing treat the request like other queries, without allowing users to +// set arbitrary query properties that may not work as anticipated. +@interface GTLRResourceURLQuery : GTLRQuery + +@property(nonatomic, strong, nullable) NSURL *resourceURL; + ++ (instancetype)queryWithResourceURL:(NSURL *)resourceURL + objectClass:(nullable Class)objectClass; + +@end + +@implementation GTLRService { + NSString *_userAgent; + NSString *_overrideUserAgent; + NSDictionary *_serviceProperties; // Properties retained for the convenience of the client app. + NSUInteger _uploadChunkSize; // Only applies to resumable chunked uploads. +} + +@synthesize additionalHTTPHeaders = _additionalHTTPHeaders, + additionalURLQueryParameters = _additionalURLQueryParameters, + allowInsecureQueries = _allowInsecureQueries, + callbackQueue = _callbackQueue, + APIKey = _apiKey, + APIKeyRestrictionBundleID = _apiKeyRestrictionBundleID, + batchPath = _batchPath, + dataWrapperRequired = _dataWrapperRequired, + fetcherService = _fetcherService, + maxRetryInterval = _maxRetryInterval, + parseQueue = _parseQueue, + prettyPrintQueryParameterNames = _prettyPrintQueryParameterNames, + resumableUploadPath = _resumableUploadPath, + retryBlock = _retryBlock, + retryEnabled = _retryEnabled, + rootURLString = _rootURLString, + servicePath = _servicePath, + shouldFetchNextPages = _shouldFetchNextPages, + simpleUploadPath = _simpleUploadPath, + objectClassResolver = _objectClassResolver, + testBlock = _testBlock, + uploadProgressBlock = _uploadProgressBlock, + userAgentAddition = _userAgentAddition; + ++ (Class)ticketClass { + return [GTLRServiceTicket class]; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _parseQueue = dispatch_queue_create("com.google.GTLRServiceParse", DISPATCH_QUEUE_SERIAL); + _callbackQueue = dispatch_get_main_queue(); + _fetcherService = [[GTMSessionFetcherService alloc] init]; + + // Make the session fetcher use a background delegate queue instead of bouncing + // through the main queue for its callbacks from NSURLSession. This should improve + // performance, and eventually be the default behavior for the fetcher. + NSOperationQueue *delegateQueue = [[NSOperationQueue alloc] init]; + delegateQueue.maxConcurrentOperationCount = 1; + delegateQueue.name = @"com.google.GTLRServiceFetcherDelegate"; + _fetcherService.sessionDelegateQueue = delegateQueue; + + NSDictionary<NSString *, Class> *kindMap = [[self class] kindStringToClassMap]; + _objectClassResolver = [GTLRObjectClassResolver resolverWithKindMap:kindMap]; + } + return self; +} + +- (NSString *)requestUserAgent { + if (_overrideUserAgent != nil) { + return _overrideUserAgent; + } + + NSString *userAgent = self.userAgent; + if (userAgent.length == 0) { + // The service instance is missing an explicit user-agent; use the bundle ID + // or process name. Don't use the bundle ID of the library's framework. + NSBundle *owningBundle = [NSBundle bundleForClass:[self class]]; + if (owningBundle == nil + || [owningBundle.bundleIdentifier isEqual:@"com.google.GTLR"]) { + owningBundle = [NSBundle mainBundle]; + } + userAgent = GTMFetcherApplicationIdentifier(owningBundle); + } + + NSString *requestUserAgent = userAgent; + + // if the user agent already specifies the library version, we'll + // use it verbatim in the request + NSString *libraryString = @"google-api-objc-client"; + NSRange libRange = [userAgent rangeOfString:libraryString + options:NSCaseInsensitiveSearch]; + if (libRange.location == NSNotFound) { + // the user agent doesn't specify the client library, so append that + // information, and the system version + NSString *libVersionString = GTLRFrameworkVersionString(); + + NSString *systemString = GTMFetcherSystemVersionString(); + + // We don't clean this with GTMCleanedUserAgentString so spaces are + // preserved + NSString *userAgentAddition = self.userAgentAddition; + NSString *customString = userAgentAddition ? + [@" " stringByAppendingString:userAgentAddition] : @""; + + // Google servers look for gzip in the user agent before sending gzip- + // encoded responses. See Service.java + requestUserAgent = [NSString stringWithFormat:@"%@ %@/%@ %@%@ (gzip)", + userAgent, libraryString, libVersionString, systemString, customString]; + } + return requestUserAgent; +} + +- (void)setMainBundleIDRestrictionWithAPIKey:(NSString *)apiKey { + self.APIKey = apiKey; + self.APIKeyRestrictionBundleID = [[NSBundle mainBundle] bundleIdentifier]; +} + +- (NSMutableURLRequest *)requestForURL:(NSURL *)url + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + ticket:(GTLRServiceTicket *)ticket { + + // subclasses may add headers to this + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:60]; + NSString *requestUserAgent = self.requestUserAgent; + [request setValue:requestUserAgent forHTTPHeaderField:@"User-Agent"]; + + if (httpMethod.length > 0) { + [request setHTTPMethod:httpMethod]; + } + + if (etag.length > 0) { + + // it's rather unexpected for an etagged object to be provided for a GET, + // but we'll check for an etag anyway, similar to HttpGDataRequest.java, + // and if present use it to request only an unchanged resource + + BOOL isDoingHTTPGet = (httpMethod == nil + || [httpMethod caseInsensitiveCompare:@"GET"] == NSOrderedSame); + + if (isDoingHTTPGet) { + + // set the etag header, even if weak, indicating we don't want + // another copy of the resource if it's the same as the object + [request setValue:etag forHTTPHeaderField:@"If-None-Match"]; + + } else { + + // if we're doing PUT or DELETE, set the etag header indicating + // we only want to update the resource if our copy matches the current + // one (unless the etag is weak and so shouldn't be a constraint at all) + BOOL isWeakETag = [etag hasPrefix:@"W/"]; + + BOOL isModifying = + [httpMethod caseInsensitiveCompare:@"PUT"] == NSOrderedSame + || [httpMethod caseInsensitiveCompare:@"DELETE"] == NSOrderedSame + || [httpMethod caseInsensitiveCompare:@"PATCH"] == NSOrderedSame; + + if (isModifying && !isWeakETag) { + [request setValue:etag forHTTPHeaderField:@"If-Match"]; + } + } + } + + return request; +} + +// objectRequestForURL returns an NSMutableURLRequest for a GTLRObject +// +// the object is the object being sent to the server, or nil; +// the http method may be nil for get, or POST, PUT, DELETE + +- (NSMutableURLRequest *)objectRequestForURL:(NSURL *)url + object:(GTLRObject *)object + contentType:(NSString *)contentType + contentLength:(NSString *)contentLength + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + additionalHeaders:(NSDictionary *)additionalHeaders + ticket:(GTLRServiceTicket *)ticket { + if (object) { + // if the object being sent has an etag, add it to the request header to + // avoid retrieving a duplicate or to avoid writing over an updated + // version of the resource on the server + // + // Typically, delete requests will provide an explicit ETag parameter, and + // other requests will have the ETag carried inside the object being updated + if (etag == nil) { + etag = ETagIfPresent(object); + } + } + + NSMutableURLRequest *request = [self requestForURL:url + ETag:etag + httpMethod:httpMethod + ticket:ticket]; + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; + + [request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"]; + + if (contentLength) { + [request setValue:contentLength forHTTPHeaderField:@"Content-Length"]; + } + + // Add the additional http headers from the service, and then from the query + NSDictionary *headers = self.additionalHTTPHeaders; + for (NSString *key in headers) { + NSString *value = [headers objectForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + headers = additionalHeaders; + for (NSString *key in headers) { + NSString *value = [headers objectForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + return request; +} + +#pragma mark - + +- (NSMutableURLRequest *)requestForQuery:(GTLRQuery *)query { + GTLR_DEBUG_ASSERT(query.bodyObject == nil, + @"requestForQuery: supports only GET methods, but was passed: %@", query); + GTLR_DEBUG_ASSERT(query.uploadParameters == nil, + @"requestForQuery: does not support uploads, but was passed: %@", query); + + NSURL *url = [self URLFromQueryObject:query + usePartialPaths:NO + includeServiceURLQueryParams:YES]; + + // If there is a developer key, add it onto the url. + NSString *apiKey = self.APIKey; + if (apiKey.length > 0) { + NSDictionary *queryParameters; + queryParameters = @{ kDeveloperAPIQueryParamKey : apiKey }; + url = [GTLRService URLWithString:url.absoluteString + queryParameters:queryParameters]; + } + + NSMutableURLRequest *request = [self requestForURL:url + ETag:nil + httpMethod:query.httpMethod + ticket:nil]; + NSString *apiRestriction = self.APIKeyRestrictionBundleID; + if ([apiRestriction length] > 0) { + [request setValue:apiRestriction forHTTPHeaderField:kXIosBundleIdHeader]; + } + + NSDictionary *headers = self.additionalHTTPHeaders; + for (NSString *key in headers) { + NSString *value = [headers objectForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + headers = query.additionalHTTPHeaders; + for (NSString *key in headers) { + NSString *value = [headers objectForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + return request; +} + +// common fetch starting method + +- (GTLRServiceTicket *)fetchObjectWithURL:(NSURL *)targetURL + objectClass:(Class)objectClass + bodyObject:(GTLRObject *)bodyObject + dataToPost:(NSData *)dataToPost + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + mayAuthorize:(BOOL)mayAuthorize + completionHandler:(GTLRServiceCompletionHandler)completionHandler + executingQuery:(id<GTLRQueryProtocol>)executingQuery + ticket:(GTLRServiceTicket *)ticket { + // Once inside this method, we should not access any service properties that may reasonably + // be changed by the app, as this method may execute multiple times during query execution + // and we want consistent behavior. Service properties should be copied to the ticket. + + GTLR_DEBUG_ASSERT(executingQuery != nil, + @"no query? service additionalURLQueryParameters needs to be added to targetURL"); + + GTLR_DEBUG_ASSERT(targetURL != nil, @"no url?"); + if (targetURL == nil) return nil; + + BOOL hasExecutionParams = [executingQuery hasExecutionParameters]; + GTLRServiceExecutionParameters *executionParams = (hasExecutionParams ? + executingQuery.executionParameters : nil); + + // We need to create a ticket unless one was created earlier (like during authentication.) + if (!ticket) { + ticket = [[[[self class] ticketClass] alloc] initWithService:self + executionParameters:executionParams]; + [ticket notifyStarting:YES]; + } + + // If there is a developer key, add it onto the URL. + NSString *apiKey = ticket.APIKey; + if (apiKey.length > 0) { + NSDictionary *queryParameters; + queryParameters = @{ kDeveloperAPIQueryParamKey : apiKey }; + targetURL = [GTLRService URLWithString:targetURL.absoluteString + queryParameters:queryParameters]; + } + + NSString *contentType = @"application/json; charset=utf-8"; + NSString *contentLength; // nil except for single-request uploads. + + if ([executingQuery isBatchQuery]) { + contentType = [NSString stringWithFormat:@"multipart/mixed; boundary=%@", + ((GTLRBatchQuery *)executingQuery).boundary]; + } + + + GTLRUploadParameters *uploadParams = executingQuery.uploadParameters; + + if (uploadParams.shouldUploadWithSingleRequest) { + NSData *uploadData = uploadParams.data; + NSString *uploadMIMEType = uploadParams.MIMEType; + if (!uploadData) { + GTLR_DEBUG_ASSERT(0, @"Uploading with a single request requires bytes to upload as NSData"); + } else { + if (uploadParams.shouldSendUploadOnly) { + contentType = uploadMIMEType; + dataToPost = uploadData; + contentLength = @(dataToPost.length).stringValue; + } else { + GTMMIMEDocument *mimeDoc = [GTMMIMEDocument MIMEDocument]; + if (dataToPost) { + // Include the object as metadata with the upload. + [mimeDoc addPartWithHeaders:@{ @"Content-Type" : contentType } + body:dataToPost]; + } + [mimeDoc addPartWithHeaders:@{ @"Content-Type" : uploadMIMEType } + body:uploadData]; + + dispatch_data_t mimeDispatchData; + unsigned long long mimeLength; + NSString *mimeBoundary; + [mimeDoc generateDispatchData:&mimeDispatchData + length:&mimeLength + boundary:&mimeBoundary]; + + contentType = [NSString stringWithFormat:@"multipart/related; boundary=%@", mimeBoundary]; + dataToPost = (NSData *)mimeDispatchData; + contentLength = @(mimeLength).stringValue; + } + } + } + + NSDictionary *additionalHeaders = nil; + NSString *restriction = self.APIKeyRestrictionBundleID; + if ([restriction length] > 0) { + additionalHeaders = @{ kXIosBundleIdHeader : restriction }; + } + + NSDictionary *queryAdditionalHeaders = executingQuery.additionalHTTPHeaders; + if (queryAdditionalHeaders) { + if (additionalHeaders) { + NSMutableDictionary *builder = [additionalHeaders mutableCopy]; + [builder addEntriesFromDictionary:queryAdditionalHeaders]; + additionalHeaders = builder; + } else { + additionalHeaders = queryAdditionalHeaders; + } + } + + NSURLRequest *request = [self objectRequestForURL:targetURL + object:bodyObject + contentType:contentType + contentLength:contentLength + ETag:etag + httpMethod:httpMethod + additionalHeaders:additionalHeaders + ticket:ticket]; + ticket.postedObject = bodyObject; + ticket.executingQuery = executingQuery; + + GTLRQuery *originalQuery = (GTLRQuery *)ticket.originalQuery; + if (originalQuery == nil) { + originalQuery = (GTLRQuery *)executingQuery; + ticket.originalQuery = originalQuery; + } + + // Some proxy servers (and some web servers) have issues with GET URLs being + // too long, trap that and move the query parameters into the body. The + // uploadParams and dataToPost should be nil for a GET, but playing it safe + // and confirming. + NSString *requestHTTPMethod = request.HTTPMethod; + BOOL isDoingHTTPGet = + (requestHTTPMethod == nil + || [requestHTTPMethod caseInsensitiveCompare:@"GET"] == NSOrderedSame); + if (isDoingHTTPGet && + (request.URL.absoluteString.length >= kMaxGETURLLength) && + (uploadParams == nil) && + (dataToPost == nil)) { + NSString *urlString = request.URL.absoluteString; + NSRange range = [urlString rangeOfString:@"?"]; + if (range.location != NSNotFound) { + NSURL *trimmedURL = [NSURL URLWithString:[urlString substringToIndex:range.location]]; + NSString *urlArgsString = [urlString substringFromIndex:(range.location + 1)]; + if (trimmedURL && (urlArgsString.length > 0)) { + dataToPost = [urlArgsString dataUsingEncoding:NSUTF8StringEncoding]; + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.URL = trimmedURL; + mutableRequest.HTTPMethod = @"POST"; + [mutableRequest setValue:@"GET" forHTTPHeaderField:@"X-HTTP-Method-Override"]; + [mutableRequest setValue:@"application/x-www-form-urlencoded" + forHTTPHeaderField:@"Content-Type"]; + [mutableRequest setValue:@(dataToPost.length).stringValue + forHTTPHeaderField:@"Content-Length"]; + request = mutableRequest; + } + } + } + ticket.fetchRequest = request; + + GTLRServiceTestBlock testBlock = ticket.testBlock; + if (testBlock) { + [self simulateFetchWithTicket:ticket + testBlock:testBlock + dataToPost:dataToPost + completionHandler:completionHandler]; + return ticket; + } + + GTMSessionFetcherService *fetcherService = ticket.fetcherService; + GTMSessionFetcher *fetcher; + + if (uploadParams == nil || uploadParams.shouldUploadWithSingleRequest) { + // Create a single-request fetcher. + fetcher = [fetcherService fetcherWithRequest:request]; + } else { + fetcher = [self uploadFetcherWithRequest:request + fetcherService:fetcherService + params:uploadParams]; + } + + if (ticket.allowInsecureQueries) { + fetcher.allowLocalhostRequest = YES; + fetcher.allowedInsecureSchemes = @[ @"http" ]; + } + + NSString *loggingName = executingQuery.loggingName; + if (loggingName.length > 0) { + NSUInteger pageNumber = ticket.pagesFetchedCounter + 1; + if (pageNumber > 1) { + loggingName = [loggingName stringByAppendingFormat:@", page %tu", pageNumber]; + } + fetcher.comment = loggingName; + } + + if (!mayAuthorize) { + fetcher.authorizer = nil; + } else { + fetcher.authorizer = ticket.authorizer; + } + + // copy the ticket's retry settings into the fetcher + fetcher.retryEnabled = ticket.retryEnabled; + fetcher.maxRetryInterval = ticket.maxRetryInterval; + + BOOL shouldExamineRetries = (ticket.retryBlock != nil); + if (shouldExamineRetries) { + GTLR_DEBUG_ASSERT(ticket.retryEnabled, @"Setting retry block without retry enabled."); + + fetcher.retryBlock = ^(BOOL suggestedWillRetry, NSError *error, + GTMSessionFetcherRetryResponse response) { + // The object fetcher may call into this retry block; this one invokes the + // selector provided by the user. + GTLRServiceRetryBlock retryBlock = ticket.retryBlock; + if (!retryBlock) { + response(suggestedWillRetry); + } else { + dispatch_group_async(ticket.callbackGroup, ticket.callbackQueue, ^{ + if (ticket.cancelled) { + response(NO); + return; + } + BOOL willRetry = retryBlock(ticket, suggestedWillRetry, error); + response(willRetry); + }); + } + }; + } + + // Remember the object fetcher in the ticket. + ticket.objectFetcher = fetcher; + + // Set the upload data. + fetcher.bodyData = dataToPost; + + // Have the fetcher call back on the parse queue. + fetcher.callbackQueue = self.parseQueue; + + // If this ticket is paging, end any ongoing background task immediately, and + // rely on the fetcher's background task now instead. + [ticket endBackgroundTask]; + + [fetcher beginFetchWithCompletionHandler:^(NSData * _Nullable data, NSError * _Nullable error) { + // We now have the JSON data for an object, or an error. + GTLR_ASSERT_CURRENT_QUEUE_DEBUG(self.parseQueue); + + // Until now, the only async operation has been the fetch, and we rely on the fetcher's + // background task on iOS to get us here if the app was backgrounded. + // + // Now we'll let the ticket create a background task so that the async parsing and call back to + // the app will happen if the app is sent to the background. The ticket is responsible for + // ending the background task. + [ticket startBackgroundTask]; + + if (ticket.cancelled) { + // If the user cancels the ticket, then cancelTicket will stop the fetcher so this + // callback probably won't occur. + // + // But just for safety, if we get here, skip any parsing steps by fabricating an error. + data = nil; + error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + userInfo:nil]; + } + + if (error == nil) { + // Successful fetch. + if (data.length > 0) { + [self prepareToParseObjectForFetcher:fetcher + executingQuery:executingQuery + ticket:ticket + error:error + defaultClass:objectClass + completionHandler:completionHandler]; + } else { + // no data (such as when deleting) + [self handleParsedObjectForFetcher:fetcher + executingQuery:executingQuery + ticket:ticket + error:nil + parsedObject:nil + hasSentParsingStartNotification:NO + completionHandler:completionHandler]; + } + return; + } + + // Failed fetch. + NSInteger status = [error code]; + if (status >= 300) { + // Return the HTTP error status code along with a more descriptive error + // from within the HTTP response payload. + NSData *responseData = fetcher.downloadedData; + if (responseData.length > 0) { + NSDictionary *responseHeaders = fetcher.responseHeaders; + NSString *responseContentType = [responseHeaders objectForKey:@"Content-Type"]; + + if (data.length > 0) { + if ([responseContentType hasPrefix:@"application/json"]) { + NSError *parseError = nil; + NSMutableDictionary *jsonWrapper = + [NSJSONSerialization JSONObjectWithData:(NSData * _Nonnull)data + options:NSJSONReadingMutableContainers + error:&parseError]; + if (parseError) { + // We could not parse the JSON payload + error = parseError; + } else { + // HTTP Streaming defined by Google services is is an array + // of requests and replies. This code never makes one of + // these requests; but, some GET apis can actually be to + // a Streaming result (for media?), so the errors can still + // come back in an array. + if ([jsonWrapper isKindOfClass:[NSArray class]]) { + NSArray *jsonWrapperAsArray = (NSArray *)jsonWrapper; +#if DEBUG + if (jsonWrapperAsArray.count > 1) { + GTLR_DEBUG_LOG(@"Got error array with >1 item, only using first. Full list: %@", + jsonWrapperAsArray); + } +#endif + // Use the first. + jsonWrapper = [jsonWrapperAsArray firstObject]; + } + + // Convert the JSON error payload into a structured error + NSMutableDictionary *errorJSON = [jsonWrapper valueForKey:@"error"]; + if (errorJSON) { + GTLRErrorObject *errorObject = [GTLRErrorObject objectWithJSON:errorJSON]; + error = [errorObject foundationError]; + } + } + } else { + // No structured JSON error was available; make a plaintext server + // error response visible in the error object. + NSString *reasonStr = [[NSString alloc] initWithData:(NSData * _Nonnull)data + encoding:NSUTF8StringEncoding]; + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : reasonStr }; + error = [NSError errorWithDomain:kGTMSessionFetcherStatusDomain + code:status + userInfo:userInfo]; + } + } else { + // Response data length is zero; we'll settle for returning the + // fetcher's error. + } + } + } + + [self handleParsedObjectForFetcher:fetcher + executingQuery:executingQuery + ticket:ticket + error:error + parsedObject:nil + hasSentParsingStartNotification:NO + completionHandler:completionHandler]; + }]; // fetcher completion handler + + // If something weird happens and the networking callbacks have been called + // already synchronously, we don't want to return the ticket since the caller + // will never know when to stop retaining it, so we'll make sure the + // success/failure callbacks have not yet been called by checking the + // ticket + if (ticket.hasCalledCallback) { + return nil; + } + + return ticket; +} + +- (GTMSessionUploadFetcher *)uploadFetcherWithRequest:(NSURLRequest *)request + fetcherService:(GTMSessionFetcherService *)fetcherService + params:(GTLRUploadParameters *)uploadParams { + // Hang on to the user's requested chunk size, and ensure it's not tiny + NSUInteger uploadChunkSize = [self serviceUploadChunkSize]; + if (uploadChunkSize < kMinimumUploadChunkSize) { + uploadChunkSize = kMinimumUploadChunkSize; + } + + NSString *uploadClassName = GTLR_CLASSNAME_STR(GTMSessionUploadFetcher); + Class uploadClass = NSClassFromString(uploadClassName); + GTLR_ASSERT(uploadClass != nil, @"GTMSessionUploadFetcher needed"); + + NSString *uploadMIMEType = uploadParams.MIMEType; + NSData *uploadData = uploadParams.data; + NSURL *uploadFileURL = uploadParams.fileURL; + NSFileHandle *uploadFileHandle = uploadParams.fileHandle; + NSURL *uploadLocationURL = uploadParams.uploadLocationURL; + + // Create the upload fetcher. + GTMSessionUploadFetcher *fetcher; + if (uploadLocationURL) { + // Resuming with the session fetcher and a file URL. + GTLR_DEBUG_ASSERT(uploadFileURL != nil, @"Resume requires a file URL"); + fetcher = [uploadClass uploadFetcherWithLocation:uploadLocationURL + uploadMIMEType:uploadMIMEType + chunkSize:(int64_t)uploadChunkSize + fetcherService:fetcherService]; + fetcher.uploadFileURL = uploadFileURL; + } else { + fetcher = [uploadClass uploadFetcherWithRequest:request + uploadMIMEType:uploadMIMEType + chunkSize:(int64_t)uploadChunkSize + fetcherService:fetcherService]; + if (uploadFileURL) { + fetcher.uploadFileURL = uploadFileURL; + } else if (uploadData) { + fetcher.uploadData = uploadData; + } else if (uploadFileHandle) { +#if DEBUG + if (uploadParams.useBackgroundSession) { + GTLR_DEBUG_LOG(@"Warning: GTLRUploadParameters should be supplied an uploadFileURL rather" + @" than a file handle to support background uploads.\n %@", uploadParams); + } +#endif + fetcher.uploadFileHandle = uploadFileHandle; + } + } + fetcher.useBackgroundSession = uploadParams.useBackgroundSession; + return fetcher; +} + +#pragma mark - + +- (GTLRServiceTicket *)executeBatchQuery:(GTLRBatchQuery *)batchObj + completionHandler:(GTLRServiceCompletionHandler)completionHandler + ticket:(GTLRServiceTicket *)ticket { + // Copy the original batch object and each query inside so our working queries cannot be modified + // by the caller, and release the callback blocks from the supplied query objects. + GTLRBatchQuery *batchCopy = [batchObj copy]; + [batchObj invalidateQuery]; + + NSArray *queries = batchCopy.queries; + NSUInteger numberOfQueries = queries.count; + if (numberOfQueries == 0) return nil; + + // Create the batch of REST calls. + NSMutableSet *requestIDs = [NSMutableSet setWithCapacity:numberOfQueries]; + NSMutableSet *loggingNames = [NSMutableSet set]; + GTMMIMEDocument *mimeDoc = [GTMMIMEDocument MIMEDocument]; + + // Each batch part has two "header" sections, an outer and inner. + // The inner headers are preceded by a line specifying the http request. + // So a part looks like this: + // + // --END_OF_PART + // Content-ID: gtlr_3 + // Content-Transfer-Encoding: binary + // Content-Type: application/http + // + // POST https://www.googleapis.com/drive/v3/files/ + // Content-Length: 0 + // Content-Type: application/json + // + // { + // "id": "04109509152946699072k" + // } + + for (GTLRQuery *query in queries) { + GTLRObject *bodyObject = query.bodyObject; + NSDictionary *bodyJSON = bodyObject.JSON; + NSString *requestID = query.requestID; + + if (requestID.length == 0) { + GTLR_DEBUG_ASSERT(0, @"Invalid query ID: %@", [query class]); + return nil; + } + + if ([requestIDs containsObject:requestID]) { + GTLR_DEBUG_ASSERT(0, @"Duplicate request ID in batch: %@", requestID); + return nil; + } + [requestIDs addObject:requestID]; + + // Create the inner request, body, and headers. + + NSURL *requestURL = [self URLFromQueryObject:query + usePartialPaths:YES + includeServiceURLQueryParams:NO]; + NSString *requestURLString = requestURL.absoluteString; + + NSError *error = nil; + NSData *bodyData; + if (bodyJSON) { + bodyData = [NSJSONSerialization dataWithJSONObject:bodyJSON + options:0 + error:&error]; + if (bodyData == nil) { + GTLR_DEBUG_ASSERT(0, @"JSON generation error: %@\n JSON: %@", error, bodyJSON); + return nil; + } + } + + NSString *httpRequestString = [NSString stringWithFormat:@"%@ %@\r\n", + query.httpMethod ?: @"GET", requestURLString]; + NSDictionary *innerPartHeaders = @{ @"Content-Type" : @"application/json", + @"Content-Length" : @(bodyData.length).stringValue }; + + innerPartHeaders = MergeDictionaries(query.additionalHTTPHeaders, innerPartHeaders); + + NSData *innerPartHeadersData = [GTMMIMEDocument dataWithHeaders:innerPartHeaders]; + + NSMutableData *innerData = + [[httpRequestString dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; + [innerData appendData:innerPartHeadersData]; + if (bodyData) { + [innerData appendData:bodyData]; + } + + // Combine the outer headers with the inner headers and body data. + NSDictionary *outerPartHeaders = @{ @"Content-Type" : @"application/http", + @"Content-ID" : requestID, + @"Content-Transfer-Encoding" : @"binary" }; + + [mimeDoc addPartWithHeaders:outerPartHeaders + body:innerData]; + + NSString *loggingName = query.loggingName ?: [[query class] description]; + [loggingNames addObject:loggingName]; + } + +#if !STRIP_GTM_FETCH_LOGGING + // Set the fetcher log comment. + if (!batchCopy.loggingName) { + NSUInteger pageNumber = ticket.pagesFetchedCounter; + NSString *pageStr = @""; + if (pageNumber > 0) { + pageStr = [NSString stringWithFormat:@"page %tu, ", pageNumber + 1]; + } + batchCopy.loggingName = [NSString stringWithFormat:@"batch: %@ (%@%tu queries)", + [loggingNames.allObjects componentsJoinedByString:@", "], + pageStr, numberOfQueries]; + } +#endif + + dispatch_data_t mimeDispatchData; + unsigned long long mimeLength; + NSString *mimeBoundary; + [mimeDoc generateDispatchData:&mimeDispatchData + length:&mimeLength + boundary:&mimeBoundary]; + + batchCopy.boundary = mimeBoundary; + + BOOL mayAuthorize = (batchCopy ? !batchCopy.shouldSkipAuthorization : YES); + + NSString *rootURLString = self.rootURLString; + NSString *batchPath = self.batchPath ?: @""; + NSString *batchURLString = [rootURLString stringByAppendingString:batchPath]; + + GTLR_DEBUG_ASSERT(![batchPath hasPrefix:@"/"], + @"batchPath shouldn't start with a slash: %@", + batchPath); + + // Query parameters override service parameters. + NSDictionary *mergedQueryParams = MergeDictionaries(self.additionalURLQueryParameters, + batchObj.additionalURLQueryParameters); + NSURL *batchURL; + if (mergedQueryParams.count > 0) { + batchURL = [GTLRService URLWithString:batchURLString + queryParameters:mergedQueryParams]; + } else { + batchURL = [NSURL URLWithString:batchURLString]; + } + + GTLRServiceTicket *resultTicket = [self fetchObjectWithURL:batchURL + objectClass:[GTLRBatchResult class] + bodyObject:nil + dataToPost:(NSData *)mimeDispatchData + ETag:nil + httpMethod:@"POST" + mayAuthorize:mayAuthorize + completionHandler:completionHandler + executingQuery:batchCopy + ticket:ticket]; + return resultTicket; +} + +#pragma mark - + +// Raw REST fetch method. + +- (GTLRServiceTicket *)fetchObjectWithURL:(NSURL *)targetURL + objectClass:(Class)objectClass + bodyObject:(GTLRObject *)bodyObject + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + mayAuthorize:(BOOL)mayAuthorize + completionHandler:(GTLRServiceCompletionHandler)completionHandler + executingQuery:(id<GTLRQueryProtocol>)executingQuery + ticket:(GTLRServiceTicket *)ticket { + // if no URL was supplied, treat this as if the fetch failed (below) + // and immediately return a nil ticket, skipping the callbacks + // + // this might be considered normal (say, updating a read-only entry + // that lacks an edit link) though higher-level calls may assert or + // return errors depending on the specific usage + if (targetURL == nil) return nil; + + NSData *dataToPost = nil; + if (bodyObject != nil && !executingQuery.uploadParameters.shouldSendUploadOnly) { + NSError *error = nil; + + NSDictionary *whatToSend; + NSDictionary *json = bodyObject.JSON; + if (json == nil) { + // Since a body object was provided, we'll ensure there's at least an empty dictionary. + json = [NSDictionary dictionary]; + } + if (_dataWrapperRequired) { + // create the top-level "data" object + whatToSend = @{ @"data" : json }; + } else { + whatToSend = json; + } + dataToPost = [NSJSONSerialization dataWithJSONObject:whatToSend + options:0 + error:&error]; + if (dataToPost == nil) { + GTLR_DEBUG_LOG(@"JSON generation error: %@", error); + } + } + + return [self fetchObjectWithURL:targetURL + objectClass:objectClass + bodyObject:bodyObject + dataToPost:dataToPost + ETag:etag + httpMethod:httpMethod + mayAuthorize:mayAuthorize + completionHandler:completionHandler + executingQuery:executingQuery + ticket:ticket]; +} + +- (void)invokeProgressCallbackForTicket:(GTLRServiceTicket *)ticket + deliveredBytes:(unsigned long long)numReadSoFar + totalBytes:(unsigned long long)total { + + GTLRServiceUploadProgressBlock block = ticket.uploadProgressBlock; + if (block) { + dispatch_group_async(ticket.callbackGroup, ticket.callbackQueue, ^{ + if (ticket.cancelled) return; + + block(ticket, numReadSoFar, total); + }); + } +} + +// Three methods handle parsing of the fetched JSON data: +// - prepareToParse posts a start notification and then spawns off parsing +// on the operation queue (if there's an operation queue) +// - parseObject does the parsing of the JSON string +// - handleParsedObject posts the stop notification and calls the callback +// with the parsed object or an error +// +// The middle method may run on a separate thread. + +- (void)prepareToParseObjectForFetcher:(GTMSessionFetcher *)fetcher + executingQuery:(id<GTLRQueryProtocol>)executingQuery + ticket:(GTLRServiceTicket *)ticket + error:(NSError *)error + defaultClass:(Class)defaultClass + completionHandler:(GTLRServiceCompletionHandler)completionHandler { + GTLR_ASSERT_CURRENT_QUEUE_DEBUG(self.parseQueue); + + [ticket postNotificationOnMainThreadWithName:kGTLRServiceTicketParsingStartedNotification + object:ticket + userInfo:nil]; + + // For unit tests to cancel during parsing, we need a synchronous notification posted. + // Because this notification is intended only for unit tests, there is no public symbol + // for the notification name. + NSNotificationCenter *nc =[NSNotificationCenter defaultCenter]; + [nc postNotificationName:@"kGTLRServiceTicketParsingStartedForTestNotification" + object:ticket + userInfo:nil]; + + NSDictionary *batchClassMap; + if ([executingQuery isBatchQuery]) { + // build a dictionary of expected classes for the batch responses + GTLRBatchQuery *batchQuery = (GTLRBatchQuery *)executingQuery; + NSArray *queries = batchQuery.queries; + batchClassMap = [NSMutableDictionary dictionaryWithCapacity:queries.count]; + for (GTLRQuery *singleQuery in queries) { + [batchClassMap setValue:singleQuery.expectedObjectClass + forKey:singleQuery.requestID]; + } + } + + [self parseObjectFromDataOfFetcher:fetcher + executingQuery:executingQuery + ticket:ticket + error:error + defaultClass:defaultClass + batchClassMap:batchClassMap + hasSentParsingStartNotification:YES + completionHandler:completionHandler]; +} + +- (void)parseObjectFromDataOfFetcher:(GTMSessionFetcher *)fetcher + executingQuery:(id<GTLRQueryProtocol>)executingQuery + ticket:(GTLRServiceTicket *)ticket + error:(NSError *)error + defaultClass:(Class)defaultClass + batchClassMap:(NSDictionary *)batchClassMap + hasSentParsingStartNotification:(BOOL)hasSentParsingStartNotification + completionHandler:(GTLRServiceCompletionHandler)completionHandler { + GTLR_ASSERT_CURRENT_QUEUE_DEBUG(self.parseQueue); + + NSError *fetchError = error; + + NSString *downloadAsDataObjectType = nil; + if (![executingQuery isBatchQuery]) { + GTLRQuery *singleQuery = (GTLRQuery *)executingQuery; + downloadAsDataObjectType = singleQuery.downloadAsDataObjectType; + } + + NSDictionary *responseHeaders = fetcher.responseHeaders; + NSString *contentType = [responseHeaders objectForKey:@"Content-Type"]; + NSData *data = fetcher.downloadedData; + BOOL hasData = data.length > 0; + BOOL isJSON = [contentType hasPrefix:@"application/json"]; + GTLRObject *parsedObject; + + if (hasData) { +#if GTLR_LOG_PERFORMANCE + NSTimeInterval secs1, secs2; + secs1 = [NSDate timeIntervalSinceReferenceDate]; +#endif + id<GTLRObjectClassResolver> objectClassResolver = ticket.objectClassResolver; + + if ((downloadAsDataObjectType.length != 0) && fetchError == nil) { + GTLRDataObject *dataObject = [GTLRDataObject object]; + dataObject.data = data; + dataObject.contentType = contentType; + parsedObject = dataObject; + } else if (isJSON) { + NSError *parseError = nil; + NSMutableDictionary *jsonWrapper = + [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&parseError]; + if (jsonWrapper == nil) { + fetchError = parseError; + } else { + NSMutableDictionary *json; + + if (_dataWrapperRequired) { + json = [jsonWrapper valueForKey:@"data"]; + } else { + json = jsonWrapper; + } + + if (json != nil) { + parsedObject = [GTLRObject objectForJSON:json + defaultClass:defaultClass + objectClassResolver:objectClassResolver]; + } + } + } else { + // Has non-JSON data; it may be batch data. + NSString *boundary; + BOOL isBatchResponse = [self isContentTypeMultipart:contentType + boundary:&boundary]; + if (isBatchResponse) { + NSArray *mimeParts = [GTMMIMEDocument MIMEPartsWithBoundary:boundary + data:data]; + NSArray *responseParts = [self responsePartsWithMIMEParts:mimeParts]; + GTLRBatchResult *batchResult = [self batchResultWithResponseParts:responseParts + batchClassMap:batchClassMap + objectClassResolver:objectClassResolver]; + parsedObject = batchResult; + } else { + GTLR_DEBUG_ASSERT(0, @"Got unexpected content type '%@'", contentType); + } + } // isJSON + +#if GTLR_LOG_PERFORMANCE + secs2 = [NSDate timeIntervalSinceReferenceDate]; + NSLog(@"allocation of %@ took %f seconds", objectClass, secs2 - secs1); +#endif + } + + [self handleParsedObjectForFetcher:fetcher + executingQuery:executingQuery + ticket:ticket + error:fetchError + parsedObject:parsedObject + hasSentParsingStartNotification:hasSentParsingStartNotification + completionHandler:completionHandler]; +} + +- (void)handleParsedObjectForFetcher:(GTMSessionFetcher *)fetcher + executingQuery:(id<GTLRQueryProtocol>)executingQuery + ticket:(GTLRServiceTicket *)ticket + error:(NSError *)error + parsedObject:(GTLRObject *)object + hasSentParsingStartNotification:(BOOL)hasSentParsingStartNotification + completionHandler:(GTLRServiceCompletionHandler)completionHandler { + GTLR_ASSERT_CURRENT_QUEUE_DEBUG(self.parseQueue); + + BOOL isResourceURLQuery = [executingQuery isKindOfClass:[GTLRResourceURLQuery class]]; + + // There may not be an object due to a fetch or parsing error + BOOL shouldFetchNextPages = ticket.shouldFetchNextPages && !isResourceURLQuery; + GTLRObject *previousObject = ticket.fetchedObject; + BOOL isFirstPage = (previousObject == nil); + + if (shouldFetchNextPages && !isFirstPage && (object != nil)) { + // Accumulate new results + object = [self mergedNewResultObject:object + oldResultObject:previousObject + forQuery:executingQuery + ticket:ticket]; + } + + ticket.fetchedObject = object; + ticket.fetchError = error; + + if (hasSentParsingStartNotification) { + // we want to always balance the start and stop notifications + [ticket postNotificationOnMainThreadWithName:kGTLRServiceTicketParsingStoppedNotification + object:ticket + userInfo:nil]; + } + + BOOL shouldCallCallbacks = YES; + + if (error == nil) { + ++ticket.pagesFetchedCounter; + + // Use the nextPageToken to fetch any later pages for non-batch queries + // + // This assumes a pagination model where objects have entries in a known "items" + // field and a "nextPageToken" field, and queries support a "pageToken" + // parameter. + + if (shouldFetchNextPages) { + // Determine if we should fetch more pages of results + + GTLRQuery *nextPageQuery = + (GTLRQuery *)[self nextPageQueryForQuery:executingQuery + result:object + ticket:ticket]; + if (nextPageQuery) { + BOOL isFetchingMore = [self fetchNextPageWithQuery:nextPageQuery + completionHandler:completionHandler + ticket:ticket]; + if (isFetchingMore) { + shouldCallCallbacks = NO; + } + } else { + // nextPageQuery == nil; no more page tokens are present + #if DEBUG && !GTLR_SKIP_PAGES_WARNING + // Each next page followed to accumulate all pages of a feed takes up to + // a few seconds. When multiple pages are being fetched, that + // usually indicates that a larger page size (that is, more items per + // feed fetched) should be requested. + // + // To avoid fetching many pages, set query.maxResults so the feed + // requested is large enough to rarely need to follow next links. + NSUInteger pageCount = ticket.pagesFetchedCounter; + if (pageCount > 2) { + NSString *queryLabel; + if ([executingQuery isBatchQuery]) { + queryLabel = @"batch query"; + } else { + queryLabel = [[executingQuery class] description]; + } + GTLR_DEBUG_LOG(@"Executing %@ query required fetching %tu pages; use a query with" + @" a larger maxResults for faster results", queryLabel, pageCount); + } + #endif + } // nextPageQuery + } else { + // !ticket.shouldFetchNextPages + #if DEBUG && !GTLR_SKIP_PAGES_WARNING + // Let the developer know that there were additional pages that would have been + // fetched if shouldFetchNextPages was enabled. + // + // The client may specify a larger page size with the query's maxResults property, + // or enable automatic pagination by turning on shouldFetchNextPages on the service + // or on the query's executionParameters. + if ([executingQuery respondsToSelector:@selector(pageToken)] + && [object isKindOfClass:[GTLRCollectionObject class]] + && [object respondsToSelector:@selector(nextPageToken)] + && object.nextPageToken.length > 0) { + GTLR_DEBUG_LOG(@"Executing %@ has additional pages of results not fetched because" + @" shouldFetchNextPages is not enabled", [executingQuery class]); + } + #endif + } // ticket.shouldFetchNextPages + } // error == nil + + if (!isFirstPage) { + // Release callbacks from this completed page's query. + [executingQuery invalidateQuery]; + } + + // We no longer care about the queries for page 2 or later, so for the client + // inspecting the ticket in the callback, the executing query should be + // the original one + ticket.executingQuery = ticket.originalQuery; + + if (!shouldCallCallbacks) { + // More fetches are happening. + } else { + dispatch_group_async(ticket.callbackGroup, ticket.callbackQueue, ^{ + // First, call query-specific callback blocks. We do this before the + // fetch callback to let applications do any final clean-up (or update + // their UI) in the fetch callback. + GTLRQuery *originalQuery = (GTLRQuery *)ticket.originalQuery; + + if (!ticket.cancelled) { + if (![originalQuery isBatchQuery]) { + // Single query + GTLRServiceCompletionHandler completionBlock = originalQuery.completionBlock; + if (completionBlock) { + completionBlock(ticket, object, error); + } + } else { + [self invokeBatchCompletionsWithTicket:ticket + batchQuery:(GTLRBatchQuery *)originalQuery + batchResult:(GTLRBatchResult *)object + error:error]; + } + + if (completionHandler) { + completionHandler(ticket, object, error); + } + ticket.hasCalledCallback = YES; + } // !ticket.cancelled + + [ticket releaseTicketCallbacks]; + [ticket endBackgroundTask]; + + // Even if the ticket has been cancelled, it should notify that it's stopped. + [ticket notifyStarting:NO]; + + // Release query callback blocks. + [originalQuery invalidateQuery]; + }); + } +} + +- (BOOL)isContentTypeMultipart:(NSString *)contentType + boundary:(NSString **)outBoundary { + NSScanner *scanner = [NSScanner scannerWithString:contentType]; + // By default, the scanner skips leading whitespace. + if ([scanner scanString:@"multipart/mixed; boundary=" intoString:NULL] + && [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] + intoString:outBoundary]) { + return YES; + } + return NO; +} + +- (NSArray <GTLRBatchResponsePart *>*)responsePartsWithMIMEParts:(NSArray <GTMMIMEDocumentPart *>*)mimeParts { + NSMutableArray *resultParts = [NSMutableArray arrayWithCapacity:mimeParts.count]; + + for (GTMMIMEDocumentPart *mimePart in mimeParts) { + GTLRBatchResponsePart *responsePart = [self responsePartWithMIMEPart:mimePart]; + [resultParts addObject:responsePart]; + } + return resultParts; +} + +- (GTLRBatchResponsePart *)responsePartWithMIMEPart:(GTMMIMEDocumentPart *)mimePart { + // The MIME part body looks like + // + // Headers (from the MIME part): + // Content-Type: application/http + // Content-ID: response-gtlr_5 + // + // Body (including inner headers): + // HTTP/1.1 200 OK + // Content-Type: application/json; charset=UTF-8 + // Date: Sat, 16 Jan 2016 18:57:05 GMT + // Expires: Sat, 16 Jan 2016 18:57:05 GMT + // Cache-Control: private, max-age=0 + // Content-Length: 13459 + // + // {"kind":"drive#fileList", ...} + + GTLRBatchResponsePart *responsePart = [[GTLRBatchResponsePart alloc] init]; + + // The only header in the actual (outer) MIME multipart headers we want is Content-ID. + // + // The content ID in the response looks like + // + // Content-ID: response-gtlr_5 + // + // but we will strip the "response-" prefix. + NSDictionary *mimeHeaders = mimePart.headers; + NSString *responseContentID = mimeHeaders[@"Content-ID"]; + if ([responseContentID hasPrefix:@"response-"]) { + responseContentID = [responseContentID substringFromIndex:@"response-".length]; + } + responsePart.contentID = responseContentID; + + // Split the body from the inner headers at the first CRLFCRLF. + NSArray <NSNumber *>*offsets; + NSData *mimePartBody = mimePart.body; + [GTMMIMEDocument searchData:mimePartBody + targetBytes:"\r\n\r\n" + targetLength:4 + foundOffsets:&offsets]; + if (offsets.count == 0) { + // Parse error. + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + [userInfo setValue:mimePartBody forKey:kGTLRServiceErrorBodyDataKey]; + [userInfo setValue:responseContentID forKey:kGTLRServiceErrorContentIDKey]; + responsePart.parseError = [NSError errorWithDomain:kGTLRServiceErrorDomain + code:GTLRServiceErrorBatchResponseUnexpected + userInfo:userInfo]; + } else { + // Separate the status/inner headers and the actual body. + NSUInteger partBodyLength = mimePartBody.length; + NSUInteger separatorOffset = offsets[0].unsignedIntegerValue; + NSData *innerHeaderData = + [mimePartBody subdataWithRange:NSMakeRange(0, (NSUInteger)separatorOffset)]; + + NSData *partBodyData; + if (separatorOffset + 4 < partBodyLength) { + NSUInteger offsetToBodyData = separatorOffset + 4; + NSUInteger bodyLength = mimePartBody.length - offsetToBodyData; + partBodyData = [mimePartBody subdataWithRange:NSMakeRange(offsetToBodyData, bodyLength)]; + } + + // Parse to separate the status line and the inner headers (though we don't + // really do much with either.) + [GTMMIMEDocument searchData:innerHeaderData + targetBytes:"\r\n" + targetLength:2 + foundOffsets:&offsets]; + if (offsets.count < 2) { + // Lack of status line and inner headers is strange, but not fatal since + // if the JSON was delivered. + GTLR_DEBUG_LOG(@"GTLRService: Batch result cannot parse headers for request %@:\n%@", + responseContentID, + [[NSString alloc] initWithData:innerHeaderData + encoding:NSUTF8StringEncoding]); + } else { + NSString *statusString; + NSInteger statusCode; + [self getResponseLineFromData:innerHeaderData + statusCode:&statusCode + statusString:&statusString]; + responsePart.statusCode = statusCode; + responsePart.statusString = statusString; + + NSUInteger actualInnerHeaderOffset = offsets[0].unsignedIntegerValue + 2; + NSData *actualInnerHeaderData; + if (innerHeaderData.length - actualInnerHeaderOffset > 0) { + NSRange actualInnerHeaderRange = + NSMakeRange(actualInnerHeaderOffset, + innerHeaderData.length - actualInnerHeaderOffset); + actualInnerHeaderData = [innerHeaderData subdataWithRange:actualInnerHeaderRange]; + } + responsePart.headers = [GTMMIMEDocument headersWithData:actualInnerHeaderData]; + } + + // Create JSON from the body. + NSError *parseError = nil; + NSMutableDictionary *json; + if (partBodyData) { + json = [NSJSONSerialization JSONObjectWithData:partBodyData + options:NSJSONReadingMutableContainers + error:&parseError]; + } else { + parseError = [NSError errorWithDomain:kGTLRServiceErrorDomain + code:GTLRServiceErrorBatchResponseUnexpected + userInfo:nil]; + } + responsePart.JSON = json; + + if (!json) { + // Add our content ID and part body data to the parse error. + NSMutableDictionary *userInfo = + [NSMutableDictionary dictionaryWithDictionary:parseError.userInfo]; + [userInfo setValue:mimePartBody forKey:kGTLRServiceErrorBodyDataKey]; + [userInfo setValue:responseContentID forKey:kGTLRServiceErrorContentIDKey]; + responsePart.parseError = [NSError errorWithDomain:parseError.domain + code:parseError.code + userInfo:userInfo]; + } + } + return responsePart; +} + +- (void)getResponseLineFromData:(NSData *)data + statusCode:(NSInteger *)outStatusCode + statusString:(NSString **)outStatusString { + // Sample response line: + // HTTP/1.1 200 OK + + *outStatusCode = -1; + *outStatusString = @"???"; + NSString *responseLine = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (!responseLine) return; + + NSScanner *scanner = [NSScanner scannerWithString:responseLine]; + // Scanner by default skips whitespace when locating the start of the next characters to + // scan. + NSCharacterSet *wsSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + NSCharacterSet *newlineSet = [NSCharacterSet newlineCharacterSet]; + NSString *httpVersion; + if ([scanner scanUpToCharactersFromSet:wsSet intoString:&httpVersion] + && [scanner scanInteger:outStatusCode] + && [scanner scanUpToCharactersFromSet:newlineSet intoString:outStatusString]) { + // Got it all. + } +} + +- (GTLRBatchResult *)batchResultWithResponseParts:(NSArray <GTLRBatchResponsePart *>*)parts + batchClassMap:(NSDictionary *)batchClassMap + objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver { + // Allow the resolver to override the batch rules class also. + Class resultClass = + GTLRObjectResolveClass(objectClassResolver, + [NSDictionary dictionary], + [GTLRBatchResult class]); + GTLRBatchResult *batchResult = [resultClass object]; + + NSMutableDictionary *successes = [NSMutableDictionary dictionary]; + NSMutableDictionary *failures = [NSMutableDictionary dictionary]; + NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionary]; + + for (GTLRBatchResponsePart *responsePart in parts) { + NSString *contentID = responsePart.contentID; + NSDictionary *json = responsePart.JSON; + NSError *parseError = responsePart.parseError; + NSInteger statusCode = responsePart.statusCode; + [responseHeaders setValue:responsePart.headers forKey:contentID]; + + if (parseError) { + GTLRErrorObject *parseErrorObject = [GTLRErrorObject objectWithFoundationError:parseError]; + [failures setValue:parseErrorObject forKey:contentID]; + } else { + // There is JSON. + NSMutableDictionary *errorJSON = [json objectForKey:@"error"]; + if (errorJSON) { + // A JSON error body should be the most informative error. + GTLRErrorObject *errorObject = [GTLRErrorObject objectWithJSON:errorJSON]; + [failures setValue:errorObject forKey:contentID]; + } else if (statusCode < 200 || statusCode > 399) { + // Report a fetch failure for this part that lacks a JSON error. + NSString *errorStr = responsePart.statusString; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey : (errorStr ?: @"<unknown>"), + }; + NSError *httpError = [NSError errorWithDomain:kGTLRServiceErrorDomain + code:GTLRServiceErrorBatchResponseStatusCode + userInfo:userInfo]; + GTLRErrorObject *httpErrorObject = [GTLRErrorObject objectWithFoundationError:httpError]; + [failures setValue:httpErrorObject forKey:contentID]; + } else { + // The JSON represents a successful response. + Class defaultClass = batchClassMap[contentID]; + id resultObject = [GTLRObject objectForJSON:[json mutableCopy] + defaultClass:defaultClass + objectClassResolver:objectClassResolver]; + if (resultObject == nil) { + // Methods like delete return no object. + resultObject = [NSNull null]; + } + [successes setValue:resultObject forKey:contentID]; + } // errorJSON + } // parseError + } // for + batchResult.successes = successes; + batchResult.failures = failures; + batchResult.responseHeaders = responseHeaders; + return batchResult; +} + +- (void)invokeBatchCompletionsWithTicket:(GTLRServiceTicket *)ticket + batchQuery:(GTLRBatchQuery *)batchQuery + batchResult:(GTLRBatchResult *)batchResult + error:(NSError *)error { + // Batch query + // + // We'll step through the queries of the original batch, not of the + // batch result + GTLR_ASSERT_CURRENT_QUEUE_DEBUG(ticket.callbackQueue); + + NSDictionary *successes = batchResult.successes; + NSDictionary *failures = batchResult.failures; + + for (GTLRQuery *oneQuery in batchQuery.queries) { + GTLRServiceCompletionHandler completionBlock = oneQuery.completionBlock; + if (completionBlock) { + // If there was no networking error, look for a query-specific + // error or result + GTLRObject *oneResult = nil; + NSError *oneError = error; + if (oneError == nil) { + NSString *requestID = [oneQuery requestID]; + GTLRErrorObject *gtlrError = [failures objectForKey:requestID]; + if (gtlrError) { + oneError = [gtlrError foundationError]; + } else { + oneResult = [successes objectForKey:requestID]; + if (oneResult == nil) { + // We found neither a success nor a failure for this query, unexpectedly. + GTLR_DEBUG_LOG(@"GTLRService: Batch result missing for request %@", + requestID); + oneError = [NSError errorWithDomain:kGTLRServiceErrorDomain + code:GTLRServiceErrorQueryResultMissing + userInfo:nil]; + } + } + } + completionBlock(ticket, oneResult, oneError); + } + } +} + +- (void)simulateFetchWithTicket:(GTLRServiceTicket *)ticket + testBlock:(GTLRServiceTestBlock)testBlock + dataToPost:(NSData *)dataToPost + completionHandler:(GTLRServiceCompletionHandler)completionHandler { + + GTLRQuery *originalQuery = (GTLRQuery *)ticket.originalQuery; + ticket.executingQuery = originalQuery; + + testBlock(ticket, ^(id testObject, NSError *testError) { + dispatch_group_async(ticket.callbackGroup, ticket.callbackQueue, ^{ + if (!ticket.cancelled) { + if (testError) { + // During simulation, we invoke any retry block, but ignore the result. + const BOOL willRetry = NO; + GTLRServiceRetryBlock retryBlock = ticket.retryBlock; + if (retryBlock) { + (void)retryBlock(ticket, willRetry, testError); + } + } else { + // Simulate upload progress, calling back up to three times. + if (ticket.uploadProgressBlock) { + GTLRQuery *query = (GTLRQuery *)ticket.originalQuery; + unsigned long long uploadLength = [self simulatedUploadLengthForQuery:query + dataToPost:dataToPost]; + unsigned long long sendReportSize = uploadLength / 3 + 1; + unsigned long long totalSentSoFar = 0; + while (totalSentSoFar < uploadLength) { + unsigned long long bytesRemaining = uploadLength - totalSentSoFar; + sendReportSize = MIN(sendReportSize, bytesRemaining); + totalSentSoFar += sendReportSize; + + [self invokeProgressCallbackForTicket:ticket + deliveredBytes:(unsigned long long)totalSentSoFar + totalBytes:(unsigned long long)uploadLength]; + } + [ticket postNotificationOnMainThreadWithName:kGTLRServiceTicketParsingStartedNotification + object:ticket + userInfo:nil]; + [ticket postNotificationOnMainThreadWithName:kGTLRServiceTicketParsingStoppedNotification + object:ticket + userInfo:nil]; + } + } + + if (![originalQuery isBatchQuery]) { + // Single query + GTLRServiceCompletionHandler completionBlock = originalQuery.completionBlock; + if (completionBlock) { + completionBlock(ticket, testObject, testError); + } + } else { + // Batch query + GTLR_DEBUG_ASSERT(!testObject || [testObject isKindOfClass:[GTLRBatchResult class]], + @"Batch queries should have result objects of type GTLRBatchResult (not %@)", + [testObject class]); + + [self invokeBatchCompletionsWithTicket:ticket + batchQuery:(GTLRBatchQuery *)originalQuery + batchResult:(GTLRBatchResult *)testObject + error:testError]; + } // isBatchQuery + + if (completionHandler) { + completionHandler(ticket, testObject, testError); + } + ticket.hasCalledCallback = YES; + } // !ticket.cancelled + + // Even if the ticket has been cancelled, it should notify that it's stopped. + [ticket notifyStarting:NO]; + + // Release query callback blocks. + [originalQuery invalidateQuery]; + }); // dispatch_group_async + }); // testBlock +} + +- (unsigned long long)simulatedUploadLengthForQuery:(GTLRQuery *)query + dataToPost:(NSData *)dataToPost { + // We're uploading the body object and other posted metadata, plus optionally the + // data or file specified in the upload parameters. + unsigned long long uploadLength = dataToPost.length; + + GTLRUploadParameters *uploadParameters = query.uploadParameters; + if (uploadParameters) { + NSData *uploadData = uploadParameters.data; + if (uploadData) { + uploadLength += uploadData.length; + } else { + NSURL *fileURL = uploadParameters.fileURL; + if (fileURL) { + NSError *fileError = nil; + NSNumber *fileSizeNum = nil; + if ([fileURL getResourceValue:&fileSizeNum + forKey:NSURLFileSizeKey + error:&fileError]) { + uploadLength += fileSizeNum.unsignedLongLongValue; + } + } else { + NSFileHandle *fileHandle = uploadParameters.fileHandle; + unsigned long long fileLength = [fileHandle seekToEndOfFile]; + uploadLength += fileLength; + } + } + } + return uploadLength; +} + +#pragma mark - + +// Given a single or batch query and its result, make a new query +// for the next pages, if any. Returns nil if there's no additional +// query to make. +// +// This method calls itself recursively to make the individual next page +// queries for a batch query. +- (id <GTLRQueryProtocol>)nextPageQueryForQuery:(id<GTLRQueryProtocol>)query + result:(GTLRObject *)object + ticket:(GTLRServiceTicket *)ticket { + if (![query isBatchQuery]) { + // This is a single query + GTLRQuery *currentPageQuery = (GTLRQuery *)query; + + // Determine if we should fetch more pages of results + GTLRQuery *nextPageQuery = nil; + NSString *nextPageToken = nil; + + if ([object respondsToSelector:@selector(nextPageToken)] + && [currentPageQuery respondsToSelector:@selector(pageToken)]) { + nextPageToken = [object performSelector:@selector(nextPageToken)]; + } + + if (nextPageToken && [object isKindOfClass:[GTLRCollectionObject class]]) { + NSString *itemsKey = [[object class] collectionItemsKey]; + GTLR_DEBUG_ASSERT(itemsKey != nil, @"Missing accumulation items key for %@", [object class]); + + SEL itemsSel = NSSelectorFromString(itemsKey); + if ([object respondsToSelector:itemsSel]) { + // Make a query for the next page, preserving the request ID + nextPageQuery = [currentPageQuery copy]; + nextPageQuery.requestID = currentPageQuery.requestID; + + [nextPageQuery performSelector:@selector(setPageToken:) + withObject:nextPageToken]; + } else { + GTLR_DEBUG_ASSERT(0, @"%@ does not implement its collection items property \"%@\"", + [object class], itemsKey); + } + } + return nextPageQuery; + } else { + // This is a batch query + // + // Check if there's a next page to fetch for any of the success + // results by invoking this method recursively on each of those results + GTLRBatchResult *batchResult = (GTLRBatchResult *)object; + GTLRBatchQuery *nextPageBatchQuery = nil; + NSDictionary *successes = batchResult.successes; + + for (NSString *requestID in successes) { + GTLRObject *singleObject = [successes objectForKey:requestID]; + GTLRQuery *singleQuery = [ticket queryForRequestID:requestID]; + + GTLRQuery *newQuery = + (GTLRQuery *)[self nextPageQueryForQuery:singleQuery + result:singleObject + ticket:ticket]; + if (newQuery) { + // There is another query to fetch + if (nextPageBatchQuery == nil) { + nextPageBatchQuery = [GTLRBatchQuery batchQuery]; + } + [nextPageBatchQuery addQuery:newQuery]; + } + } + return nextPageBatchQuery; + } +} + +// When a ticket is set to fetch more pages for feeds, this routine +// initiates the fetch for each additional feed page +// +// Returns YES if fetching of the next page has started. +- (BOOL)fetchNextPageWithQuery:(GTLRQuery *)query + completionHandler:(GTLRServiceCompletionHandler)handler + ticket:(GTLRServiceTicket *)ticket { + // Sanity check the number of pages fetched already + if (ticket.pagesFetchedCounter > kMaxNumberOfNextPagesFetched) { + // Sanity check failed: way too many pages were fetched, so the query's + // page size should be bigger to avoid driving up networking and server + // overhead. + // + // The client should be querying with a higher max results per page + // to avoid this. + GTLR_DEBUG_ASSERT(0, @"Fetched too many next pages executing %@;" + @" increase maxResults page size to avoid this.", + [query class]); + return NO; + } + + GTLRServiceTicket *newTicket; + if ([query isBatchQuery]) { + newTicket = [self executeBatchQuery:(GTLRBatchQuery *)query + completionHandler:handler + ticket:ticket]; + } else { + BOOL mayAuthorize = !query.shouldSkipAuthorization; + NSURL *url = [self URLFromQueryObject:query + usePartialPaths:NO + includeServiceURLQueryParams:YES]; + newTicket = [self fetchObjectWithURL:url + objectClass:query.expectedObjectClass + bodyObject:query.bodyObject + ETag:nil + httpMethod:query.httpMethod + mayAuthorize:mayAuthorize + completionHandler:handler + executingQuery:query + ticket:ticket]; + } + + // In the bizarre case that the fetch didn't begin, newTicket will be + // nil. So long as the new ticket is the same as the ticket we're + // continuing, then we're happy. + GTLR_ASSERT(newTicket == ticket || newTicket == nil, + @"Pagination should not create an additional ticket: %@", newTicket); + + BOOL isFetchingNextPageWithCurrentTicket = (newTicket == ticket); + return isFetchingNextPageWithCurrentTicket; +} + +// Given a new single or batch result (meaning additional pages for a previous +// query result), merge it into the old result, and return the updated object. +// +// For a single result, this inserts the old result items into the new result. +// For batch results, this replaces some of the old items with new items. +// +// This method changes the objects passed in (the old result for batches, the new result +// for individual objects.) +- (GTLRObject *)mergedNewResultObject:(GTLRObject *)newResult + oldResultObject:(GTLRObject *)oldResult + forQuery:(id<GTLRQueryProtocol>)query + ticket:(GTLRServiceTicket *)ticket { + GTLR_DEBUG_ASSERT([oldResult isMemberOfClass:[newResult class]], + @"Trying to merge %@ and %@", [oldResult class], [newResult class]); + + if ([query isBatchQuery]) { + // Batch query result + // + // The new batch results are a subset of the old result's queries, since + // not all queries in the batch necessarily have additional pages. + // + // New success objects replace old success objects, with the old items + // prepended; new failure objects replace old success objects. + // We will update the old batch results with accumulated items, using the + // new objects, and return the old batch. + // + // We reuse the old batch results object because it may include some earlier + // results which did not have additional pages. + GTLRBatchResult *newBatchResult = (GTLRBatchResult *)newResult; + GTLRBatchResult *oldBatchResult = (GTLRBatchResult *)oldResult; + + NSDictionary *newSuccesses = newBatchResult.successes; + if (newSuccesses.count > 0) { + NSDictionary *oldSuccesses = oldBatchResult.successes; + NSMutableDictionary *mutableOldSuccesses = [oldSuccesses mutableCopy]; + + for (NSString *requestID in newSuccesses) { + GTLRObject *newObj = [newSuccesses objectForKey:requestID]; + GTLRObject *oldObj = [oldSuccesses objectForKey:requestID]; + + GTLRQuery *thisQuery = [ticket queryForRequestID:requestID]; + + // Recursively merge the single query's result object, appending new items to the old items. + GTLRObject *updatedObj = [self mergedNewResultObject:newObj + oldResultObject:oldObj + forQuery:thisQuery + ticket:ticket]; + + // In the old batch, replace the old result object with the new one. + [mutableOldSuccesses setObject:updatedObj forKey:requestID]; + } // for requestID + oldBatchResult.successes = mutableOldSuccesses; + } // newSuccesses.count > 0 + + NSDictionary *newFailures = newBatchResult.failures; + if (newFailures.count > 0) { + NSMutableDictionary *mutableOldSuccesses = [oldBatchResult.successes mutableCopy]; + NSMutableDictionary *mutableOldFailures = [oldBatchResult.failures mutableCopy]; + for (NSString *requestID in newFailures) { + // In the old batch, replace old successes or failures with the new failure. + GTLRErrorObject *newError = [newFailures objectForKey:requestID]; + [mutableOldFailures setObject:newError forKey:requestID]; + + [mutableOldSuccesses removeObjectForKey:requestID]; + } + oldBatchResult.failures = mutableOldFailures; + oldBatchResult.successes = mutableOldSuccesses; + } // newFailures.count > 0 + return oldBatchResult; + } else { + // Single query result + // + // Merge the items into the new object, and return the new object. + NSString *itemsKey = [[oldResult class] collectionItemsKey]; + + GTLR_DEBUG_ASSERT([oldResult respondsToSelector:NSSelectorFromString(itemsKey)], + @"Collection items key \"%@\" not implemented by %@", itemsKey, oldResult); + if (itemsKey) { + // Append the new items to the old items. + NSArray *oldItems = [oldResult valueForKey:itemsKey]; + NSArray *newItems = [newResult valueForKey:itemsKey]; + NSMutableArray *items = [NSMutableArray arrayWithArray:oldItems]; + [items addObjectsFromArray:newItems]; + [newResult setValue:items forKey:itemsKey]; + } else { + // This shouldn't happen. + newResult = oldResult; + } + return newResult; + } +} + +#pragma mark - + +// GTLRQuery methods. + +// Helper to create the URL from the parts. +- (NSURL *)URLFromQueryObject:(GTLRQuery *)query + usePartialPaths:(BOOL)usePartialPaths + includeServiceURLQueryParams:(BOOL)includeServiceURLQueryParams { + NSString *rootURLString = self.rootURLString; + + // Skip URI template expansion if the resource URL was provided. + if ([query isKindOfClass:[GTLRResourceURLQuery class]]) { + // Because the query is created by the service rather than by the user, + // query.additionalURLQueryParameters must be nil, and usePartialPaths + // is irrelevant as the query is not in a batch. + GTLR_DEBUG_ASSERT(!usePartialPaths, + @"Batch not supported with resource URL fetch"); + GTLR_DEBUG_ASSERT(!query.uploadParameters && !query.useMediaDownloadService + && !query.downloadAsDataObjectType && !query.additionalURLQueryParameters, + @"Unsupported query properties"); + NSURL *result = ((GTLRResourceURLQuery *)query).resourceURL; + if (includeServiceURLQueryParams) { + NSDictionary *additionalParams = self.additionalURLQueryParameters; + if (additionalParams.count) { + result = [GTLRService URLWithString:result.absoluteString + queryParameters:additionalParams]; + } + } + return result; + } + + // This is all the dance needed due to having query and path parameters for + // REST based queries. + NSDictionary *params = query.JSON; + NSString *queryFilledPathURI = [GTLRURITemplate expandTemplate:query.pathURITemplate + values:params]; + + // Per https://developers.google.com/discovery/v1/using#build-compose and + // https://developers.google.com/discovery/v1/using#discovery-doc-methods-mediadownload + // glue together the parts. + NSString *servicePath = self.servicePath ?: @""; + NSString *uploadPath = @""; + NSString *downloadPath = @""; + + GTLR_DEBUG_ASSERT([rootURLString hasSuffix:@"/"], + @"rootURLString should end in a slash: %@", rootURLString); + GTLR_DEBUG_ASSERT(((servicePath.length == 0) || + (![servicePath hasPrefix:@"/"] && [servicePath hasSuffix:@"/"])), + @"servicePath shouldn't start with a slash but should end with one: %@", + servicePath); + GTLR_DEBUG_ASSERT(![query.pathURITemplate hasPrefix:@"/"], + @"the queries's pathURITemplate should not start with a slash: %@", + query.pathURITemplate); + + GTLRUploadParameters *uploadParameters = query.uploadParameters; + if (uploadParameters != nil) { + // If there is an override, clear all the parts and just use it with the + // the rootURLString. + NSString *override = (uploadParameters.shouldUploadWithSingleRequest + ? query.simpleUploadPathURITemplateOverride + : query.resumableUploadPathURITemplateOverride); + if (override.length > 0) { + GTLR_DEBUG_ASSERT(![override hasPrefix:@"/"], + @"The query's %@UploadPathURITemplateOverride should not start with a slash: %@", + (uploadParameters.shouldUploadWithSingleRequest ? @"simple" : @"resumable"), + override); + queryFilledPathURI = [GTLRURITemplate expandTemplate:override + values:params]; + servicePath = @""; + } else { + if (uploadParameters.shouldUploadWithSingleRequest) { + uploadPath = self.simpleUploadPath ?: @""; + } else { + uploadPath = self.resumableUploadPath ?: @""; + } + GTLR_DEBUG_ASSERT(((uploadPath.length == 0) || + (![uploadPath hasPrefix:@"/"] && + [uploadPath hasSuffix:@"/"])), + @"%@UploadPath shouldn't start with a slash but should end with one: %@", + (uploadParameters.shouldUploadWithSingleRequest ? @"simple" : @"resumable"), + uploadPath); + } + } + + if (query.useMediaDownloadService && + (query.downloadAsDataObjectType.length > 0)) { + downloadPath = @"download/"; + GTLR_DEBUG_ASSERT(uploadPath.length == 0, + @"Uploading while also downloading via mediaDownService" + @" is not well defined."); + } + + if (usePartialPaths) rootURLString = @"/"; + + NSString *urlString = + [NSString stringWithFormat:@"%@%@%@%@%@", + rootURLString, downloadPath, uploadPath, servicePath, queryFilledPathURI]; + + // Remove the path parameters from the dictionary. + NSMutableDictionary *workingQueryParams = [NSMutableDictionary dictionaryWithDictionary:params]; + + NSArray *pathParameterNames = query.pathParameterNames; + if (pathParameterNames.count > 0) { + [workingQueryParams removeObjectsForKeys:pathParameterNames]; + } + + // Note: A developer can override the uploadType and alt query parameters via + // query.additionalURLQueryParameters since those are added afterwards. + if (uploadParameters.shouldUploadWithSingleRequest) { + NSString *uploadType = uploadParameters.shouldSendUploadOnly ? @"media" : @"multipart"; + [workingQueryParams setObject:uploadType forKey:@"uploadType"]; + } + NSString *downloadAsDataObjectType = query.downloadAsDataObjectType; + if (downloadAsDataObjectType.length > 0) { + [workingQueryParams setObject:downloadAsDataObjectType + forKey:@"alt"]; + } + + // Add any parameters the user added directly to the query. + NSDictionary *mergedParams = MergeDictionaries(workingQueryParams, + query.additionalURLQueryParameters); + if (includeServiceURLQueryParams) { + // Query parameters override service parameters. + mergedParams = MergeDictionaries(self.additionalURLQueryParameters, mergedParams); + } + + NSURL *result = [GTLRService URLWithString:urlString + queryParameters:mergedParams]; + return result; +} + +- (GTLRServiceTicket *)executeQuery:(id<GTLRQueryProtocol>)queryObj + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + GTMSessionFetcherAssertValidSelector(delegate, finishedSelector, + @encode(GTLRServiceTicket *), @encode(GTLRObject *), @encode(NSError *), 0); + GTLRServiceCompletionHandler completionHandler = ^(GTLRServiceTicket *ticket, + id object, + NSError *error) { + if (delegate && finishedSelector) { + NSMethodSignature *sig = [delegate methodSignatureForSelector:finishedSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:(SEL)finishedSelector]; + [invocation setTarget:delegate]; + [invocation setArgument:&ticket atIndex:2]; + [invocation setArgument:&object atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } + }; + return [self executeQuery:queryObj completionHandler:completionHandler]; +} + +- (GTLRServiceTicket *)executeQuery:(id<GTLRQueryProtocol>)queryObj + completionHandler:(void (^)(GTLRServiceTicket *ticket, id object, + NSError *error))handler { + if ([queryObj isBatchQuery]) { + GTLR_DEBUG_ASSERT([queryObj isKindOfClass:[GTLRBatchQuery class]], + @"GTLRBatchQuery required for batches (passed %@)", + [queryObj class]); + return [self executeBatchQuery:(GTLRBatchQuery *)queryObj + completionHandler:handler + ticket:nil]; + } + GTLR_DEBUG_ASSERT([queryObj isKindOfClass:[GTLRQuery class]], + @"GTLRQuery required for single queries (passed %@)", + [queryObj class]); + + // Copy the original query so our working query cannot be modified by the caller, + // and release the callback blocks from the supplied query object. + GTLRQuery *query = [(GTLRQuery *)queryObj copy]; + + GTLR_DEBUG_ASSERT(!query.queryInvalid, @"Query has already been executed: %@", query); + [queryObj invalidateQuery]; + + // For individual queries, we rely on the fetcher's log formatting so pretty-printing + // is not needed. Developers may override this in the query's additionalURLQueryParameters. + NSArray *prettyPrintNames = self.prettyPrintQueryParameterNames; + NSString *firstPrettyPrintName = prettyPrintNames.firstObject; + if (firstPrettyPrintName && (query.downloadAsDataObjectType.length == 0) + && ![query isKindOfClass:[GTLRResourceURLQuery class]]) { + NSDictionary *queryParams = query.additionalURLQueryParameters; + BOOL foundOne = NO; + for (NSString *name in prettyPrintNames) { + if ([queryParams objectForKey:name] != nil) { + foundOne = YES; + break; + } + } + if (!foundOne) { + NSMutableDictionary *worker = + [NSMutableDictionary dictionaryWithDictionary:queryParams]; + [worker setObject:@"false" forKey:firstPrettyPrintName]; + query.additionalURLQueryParameters = worker; + } + } + + BOOL mayAuthorize = !query.shouldSkipAuthorization; + NSURL *url = [self URLFromQueryObject:query + usePartialPaths:NO + includeServiceURLQueryParams:YES]; + + return [self fetchObjectWithURL:url + objectClass:query.expectedObjectClass + bodyObject:query.bodyObject + ETag:nil + httpMethod:query.httpMethod + mayAuthorize:mayAuthorize + completionHandler:handler + executingQuery:query + ticket:nil]; +} + +- (GTLRServiceTicket *)fetchObjectWithURL:(NSURL *)resourceURL + objectClass:(nullable Class)objectClass + executionParameters:(nullable GTLRServiceExecutionParameters *)executionParameters + completionHandler:(nullable GTLRServiceCompletionHandler)handler { + GTLRResourceURLQuery *query = [GTLRResourceURLQuery queryWithResourceURL:resourceURL + objectClass:objectClass]; + query.executionParameters = executionParameters; + + return [self executeQuery:query + completionHandler:handler]; +} + +#pragma mark - + +- (NSString *)userAgent { + return _userAgent; +} + +- (void)setExactUserAgent:(NSString *)userAgent { + _userAgent = [userAgent copy]; +} + +- (void)setUserAgent:(NSString *)userAgent { + // remove whitespace and unfriendly characters + NSString *str = GTMFetcherCleanedUserAgentString(userAgent); + [self setExactUserAgent:str]; +} + +- (void)overrideRequestUserAgent:(nullable NSString *)requestUserAgent { + _overrideUserAgent = [requestUserAgent copy]; +} + +#pragma mark - + ++ (NSDictionary<NSString *, Class> *)kindStringToClassMap { + // Generated services will provide custom ones. + return [NSDictionary dictionary]; +} + +#pragma mark - + +// The service properties becomes the initial value for each future ticket's +// properties +- (void)setServiceProperties:(NSDictionary *)dict { + _serviceProperties = [dict copy]; +} + +- (NSDictionary *)serviceProperties { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + __autoreleasing id props = _serviceProperties; + return props; +} + +- (void)setAuthorizer:(id <GTMFetcherAuthorizationProtocol>)authorizer { + self.fetcherService.authorizer = authorizer; +} + +- (id <GTMFetcherAuthorizationProtocol>)authorizer { + return self.fetcherService.authorizer; +} + ++ (NSUInteger)defaultServiceUploadChunkSize { + // Subclasses may override this method. + + // The upload server prefers multiples of 256K. + const NSUInteger kMegabyte = 4 * 256 * 1024; + +#if TARGET_OS_IPHONE + // For iOS, we're balancing a large upload size with limiting the memory + // used for the upload data buffer. + return 4 * kMegabyte; +#else + // A large upload chunk size minimizes http overhead and server effort. + return 25 * kMegabyte; +#endif +} + +- (NSUInteger)serviceUploadChunkSize { + if (_uploadChunkSize > 0) { + return _uploadChunkSize; + } + return [[self class] defaultServiceUploadChunkSize]; +} + +- (void)setServiceUploadChunkSize:(NSUInteger)val { + _uploadChunkSize = val; +} + +- (void)setSurrogates:(NSDictionary <Class, Class>*)surrogates { + NSDictionary *kindMap = [[self class] kindStringToClassMap]; + + self.objectClassResolver = [GTLRObjectClassResolver resolverWithKindMap:kindMap + surrogates:surrogates]; +} + +#pragma mark - Internal helper + +// If there are already query parameters on urlString, the new ones are simply +// appended after them. ++ (NSURL *)URLWithString:(NSString *)urlString + queryParameters:(NSDictionary *)queryParameters { + if (urlString.length == 0) return nil; + + NSString *fullURLString; + if (queryParameters.count > 0) { + // Use GTLRURITemplate by building up a template and then feeding in the + // values. The template is query expansion ('?'), and any key that is + // an array or dictionary gets tagged to explode them ('+'). + NSArray *sortedQueryParamKeys = + [queryParameters.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + + NSMutableString *template = [@"{" mutableCopy]; + char joiner = '?'; + for (NSString *key in sortedQueryParamKeys) { + [template appendFormat:@"%c%@", joiner, key]; + id value = [queryParameters objectForKey:key]; + if ([value isKindOfClass:[NSArray class]] || + [value isKindOfClass:[NSDictionary class]]) { + [template appendString:@"+"]; + } + joiner = ','; + } + [template appendString:@"}"]; + NSString *urlArgs = + [GTLRURITemplate expandTemplate:template + values:queryParameters]; + urlArgs = [urlArgs substringFromIndex:1]; // Drop the '?' and use the joiner. + + BOOL missingQMark = ([urlString rangeOfString:@"?"].location == NSNotFound); + joiner = missingQMark ? '?' : '&'; + fullURLString = + [NSString stringWithFormat:@"%@%c%@", urlString, joiner, urlArgs]; + } else { + fullURLString = urlString; + } + NSURL *result = [NSURL URLWithString:fullURLString]; + return result; +} + +@end + +@implementation GTLRService (TestingSupport) + ++ (instancetype)mockServiceWithFakedObject:(id)objectOrNil + fakedError:(NSError *)errorOrNil { + GTLRService *service = [[GTLRService alloc] init]; + service.rootURLString = @"https://example.invalid/"; + service.testBlock = ^(GTLRServiceTicket *ticket, GTLRServiceTestResponse testResponse) { + testResponse(objectOrNil, errorOrNil); + }; + return service; +} + +- (BOOL)waitForTicket:(GTLRServiceTicket *)ticket + timeout:(NSTimeInterval)timeoutInSeconds { + // Loop until the fetch completes or is cancelled, or until the timeout has expired. + NSDate *giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + + BOOL hasTimedOut = NO; + while (1) { + int64_t delta = (int64_t)(100 * NSEC_PER_MSEC); // 100 ms + BOOL areCallbacksPending = + (dispatch_group_wait(ticket.callbackGroup, dispatch_time(DISPATCH_TIME_NOW, delta)) != 0); + + if (!areCallbacksPending && (ticket.hasCalledCallback || ticket.cancelled)) break; + + hasTimedOut = (giveUpDate.timeIntervalSinceNow <= 0); + if (hasTimedOut) { + if (areCallbacksPending) { + // A timeout while waiting for the dispatch group to finish is seriously unexpected. + GTLR_DEBUG_LOG(@"%s timed out while waiting for the dispatch group", __PRETTY_FUNCTION__); + } else { + GTLR_DEBUG_LOG(@"%s timed out without callbacks pending", __PRETTY_FUNCTION__); + } + break; + } + + // Run the current run loop 1/1000 of a second to give the networking + // code a chance to work. + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } + return !hasTimedOut; +} + +@end + +@implementation GTLRServiceTicket { + GTLRService *_service; + NSDictionary *_ticketProperties; + GTLRServiceUploadProgressBlock _uploadProgressBlock; + BOOL _needsStopNotification; +} + +@synthesize APIKey = _apiKey, + APIKeyRestrictionBundleID = _apiKeyRestrictionBundleID, + allowInsecureQueries = _allowInsecureQueries, + authorizer = _authorizer, + cancelled = _cancelled, + callbackGroup = _callbackGroup, + callbackQueue = _callbackQueue, + creationDate = _creationDate, + executingQuery = _executingQuery, + fetchedObject = _fetchedObject, + fetchError = _fetchError, + fetchRequest = _fetchRequest, + fetcherService = _fetcherService, + hasCalledCallback = _hasCalledCallback, + maxRetryInterval = _maxRetryInterval, + objectFetcher = _objectFetcher, + originalQuery = _originalQuery, + pagesFetchedCounter = _pagesFetchedCounter, + postedObject = _postedObject, + retryBlock = _retryBlock, + retryEnabled = _retryEnabled, + shouldFetchNextPages = _shouldFetchNextPages, + objectClassResolver = _objectClassResolver, + testBlock = _testBlock; + +#if GTM_BACKGROUND_TASK_FETCHING +@synthesize backgroundTaskIdentifier = _backgroundTaskIdentifier; +#endif + +#if DEBUG +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + self = nil; + return self; +} +#endif + +#if GTM_BACKGROUND_TASK_FETCHING && DEBUG +- (void)dealloc { + GTLR_DEBUG_ASSERT(_backgroundTaskIdentifier == UIBackgroundTaskInvalid, + @"Background task not ended"); +} +#endif // GTM_BACKGROUND_TASK_FETCHING && DEBUG + + +- (instancetype)initWithService:(GTLRService *)service + executionParameters:(GTLRServiceExecutionParameters *)params { + self = [super init]; + if (self) { + // ivars set at init time and never changed are exposed as atomic readonly properties. + _service = service; + _fetcherService = service.fetcherService; + _authorizer = service.authorizer; + + _ticketProperties = MergeDictionaries(service.serviceProperties, params.ticketProperties); + + _objectClassResolver = params.objectClassResolver ?: service.objectClassResolver; + + _retryEnabled = ((params.retryEnabled != nil) ? params.retryEnabled.boolValue : service.retryEnabled); + _maxRetryInterval = ((params.maxRetryInterval != nil) ? + params.maxRetryInterval.doubleValue : service.maxRetryInterval); + _shouldFetchNextPages = ((params.shouldFetchNextPages != nil)? + params.shouldFetchNextPages.boolValue : service.shouldFetchNextPages); + + GTLRServiceUploadProgressBlock uploadProgressBlock = + params.uploadProgressBlock ?: service.uploadProgressBlock; + _uploadProgressBlock = [uploadProgressBlock copy]; + + GTLRServiceRetryBlock retryBlock = params.retryBlock ?: service.retryBlock; + _retryBlock = [retryBlock copy]; + if (_retryBlock) { + _retryEnabled = YES; + } + + _testBlock = params.testBlock ?: service.testBlock; + + _callbackQueue = ((_Nonnull dispatch_queue_t)params.callbackQueue) ?: service.callbackQueue; + _callbackGroup = dispatch_group_create(); + + _apiKey = [service.APIKey copy]; + _apiKeyRestrictionBundleID = [service.APIKeyRestrictionBundleID copy]; + _allowInsecureQueries = service.allowInsecureQueries; + +#if GTM_BACKGROUND_TASK_FETCHING + _backgroundTaskIdentifier = UIBackgroundTaskInvalid; +#endif + + _creationDate = [NSDate date]; + } + return self; +} + +- (NSString *)description { + NSString *devKeyInfo = @""; + if (_apiKey != nil) { + devKeyInfo = [NSString stringWithFormat:@" devKey:%@", _apiKey]; + } + NSString *keyRestrictionInfo = @""; + if (_apiKeyRestrictionBundleID != nil) { + keyRestrictionInfo = [NSString stringWithFormat:@" restriction:%@", + _apiKeyRestrictionBundleID]; + } + + NSString *authorizerInfo = @""; + id <GTMFetcherAuthorizationProtocol> authorizer = self.objectFetcher.authorizer; + if (authorizer != nil) { + authorizerInfo = [NSString stringWithFormat:@" authorizer:%@", authorizer]; + } + + return [NSString stringWithFormat:@"%@ %p: {service:%@%@%@%@ fetcher:%@ }", + [self class], self, + _service, devKeyInfo, keyRestrictionInfo, authorizerInfo, _objectFetcher]; +} + +- (void)postNotificationOnMainThreadWithName:(NSString *)name + object:(id)object + userInfo:(NSDictionary *)userInfo { + // We always post these async to ensure they remain in order. + dispatch_group_async(self.callbackGroup, dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:name + object:object + userInfo:userInfo]; + }); +} + +- (void)pauseUpload { + GTMSessionFetcher *fetcher = self.objectFetcher; + BOOL canPause = [fetcher respondsToSelector:@selector(pauseFetching)]; + GTLR_DEBUG_ASSERT(canPause, @"tickets can be paused only for chunked resumable uploads"); + + if (canPause) { + [(GTMSessionUploadFetcher *)fetcher pauseFetching]; + } +} + +- (void)resumeUpload { + GTMSessionFetcher *fetcher = self.objectFetcher; + BOOL canResume = [fetcher respondsToSelector:@selector(resumeFetching)]; + GTLR_DEBUG_ASSERT(canResume, @"tickets can be resumed only for chunked resumable uploads"); + + if (canResume) { + [(GTMSessionUploadFetcher *)fetcher resumeFetching]; + } +} + +- (BOOL)isUploadPaused { + BOOL isPausable = [_objectFetcher respondsToSelector:@selector(isPaused)]; + GTLR_DEBUG_ASSERT(isPausable, @"tickets can be paused only for chunked resumable uploads"); + + if (isPausable) { + return [(GTMSessionUploadFetcher *)_objectFetcher isPaused]; + } + return NO; +} + +- (BOOL)isCancelled { + @synchronized(self) { + return _cancelled; + } +} + +- (void)cancelTicket { + @synchronized(self) { + _cancelled = YES; + } + + [_objectFetcher stopFetching]; + + self.objectFetcher = nil; + self.fetchRequest = nil; + _ticketProperties = nil; + + [self releaseTicketCallbacks]; + [self endBackgroundTask]; + + [self.executingQuery invalidateQuery]; + + id<GTLRQueryProtocol> originalQuery = self.originalQuery; + self.executingQuery = originalQuery; + [originalQuery invalidateQuery]; + + _service = nil; + _fetcherService = nil; + _authorizer = nil; + _testBlock = nil; +} + +#if GTM_BACKGROUND_TASK_FETCHING +// When the fetcher's substitute UIApplication object is present, GTLRService +// will use that instead of UIApplication. This is just to reduce duplicating +// that plumbing for testing. ++ (nullable id<GTMUIApplicationProtocol>)fetcherUIApplication { + id<GTMUIApplicationProtocol> app = [GTMSessionFetcher substituteUIApplication]; + if (app) return app; + + static Class applicationClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + BOOL isAppExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]; + if (!isAppExtension) { + Class cls = NSClassFromString(@"UIApplication"); + if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) { + applicationClass = cls; + } + } + }); + + if (applicationClass) { + app = (id<GTMUIApplicationProtocol>)[applicationClass sharedApplication]; + } + return app; +} +#endif // GTM_BACKGROUND_TASK_FETCHING + +- (void)startBackgroundTask { +#if GTM_BACKGROUND_TASK_FETCHING + GTLR_DEBUG_ASSERT(self.backgroundTaskIdentifier == UIBackgroundTaskInvalid, + @"Redundant GTLRService background task: %tu", self.backgroundTaskIdentifier); + + NSString *taskName = [[self.executingQuery class] description]; + + id<GTMUIApplicationProtocol> app = [[self class] fetcherUIApplication]; + + // We'll use a locally-scoped task ID variable so the expiration block is guaranteed + // to refer to this task rather than to whatever task the property has. + __block UIBackgroundTaskIdentifier bgTaskID = + [app beginBackgroundTaskWithName:taskName + expirationHandler:^{ + // Background task expiration callback. This block is always invoked by + // UIApplication on the main thread. + if (bgTaskID != UIBackgroundTaskInvalid) { + @synchronized(self) { + if (bgTaskID == self.backgroundTaskIdentifier) { + self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + } + // This explicitly ends the captured bgTaskID rather than the backgroundTaskIdentifier + // property to ensure expiration is handled even if the property has changed. + [app endBackgroundTask:bgTaskID]; + } + }]; + @synchronized(self) { + self.backgroundTaskIdentifier = bgTaskID; + } +#endif // GTM_BACKGROUND_TASK_FETCHING +} + +- (void)endBackgroundTask { +#if GTM_BACKGROUND_TASK_FETCHING + // Whenever the connection stops or a next page is about to be fetched, + // tell UIApplication we're done. + UIBackgroundTaskIdentifier bgTaskID; + @synchronized(self) { + bgTaskID = self.backgroundTaskIdentifier; + self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + if (bgTaskID != UIBackgroundTaskInvalid) { + [[[self class] fetcherUIApplication] endBackgroundTask:bgTaskID]; + } +#endif // GTM_BACKGROUND_TASK_FETCHING +} + +- (void)releaseTicketCallbacks { + self.uploadProgressBlock = nil; + self.retryBlock = nil; +} + +- (void)notifyStarting:(BOOL)isStarting { + GTLR_DEBUG_ASSERT(!GTLR_AreBoolsEqual(isStarting, _needsStopNotification), + @"Notification mismatch (isStarting=%d)", isStarting); + if (GTLR_AreBoolsEqual(isStarting, _needsStopNotification)) return; + + NSString *name; + if (isStarting) { + name = kGTLRServiceTicketStartedNotification; + _needsStopNotification = YES; + } else { + name = kGTLRServiceTicketStoppedNotification; + _needsStopNotification = NO; + } + [self postNotificationOnMainThreadWithName:name + object:self + userInfo:nil]; +} + +- (id)service { + return _service; +} + +- (void)setObjectFetcher:(GTMSessionFetcher *)fetcher { + @synchronized(self) { + _objectFetcher = fetcher; + } + + [self updateObjectFetcherProgressCallbacks]; +} + +- (GTMSessionFetcher *)objectFetcher { + @synchronized(self) { + return _objectFetcher; + } +} + +- (NSDictionary *)ticketProperties { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + __autoreleasing id props = _ticketProperties; + return props; +} + +- (GTLRServiceUploadProgressBlock)uploadProgressBlock { + return _uploadProgressBlock; +} + +- (void)setUploadProgressBlock:(GTLRServiceUploadProgressBlock)block { + if (_uploadProgressBlock != block) { + _uploadProgressBlock = [block copy]; + + [self updateObjectFetcherProgressCallbacks]; + } +} + +- (void)updateObjectFetcherProgressCallbacks { + // Internal method. Do not override. + GTMSessionFetcher *fetcher = [self objectFetcher]; + + if (_uploadProgressBlock) { + // Use a local block variable to avoid a spurious retain cycle warning. + GTMSessionFetcherSendProgressBlock fetcherSentDataBlock = ^(int64_t bytesSent, + int64_t totalBytesSent, + int64_t totalBytesExpectedToSend) { + [self->_service invokeProgressCallbackForTicket:self + deliveredBytes:(unsigned long long)totalBytesSent + totalBytes:(unsigned long long)totalBytesExpectedToSend]; + }; + + fetcher.sendProgressBlock = fetcherSentDataBlock; + } else { + fetcher.sendProgressBlock = nil; + } +} + +- (NSInteger)statusCode { + return [_objectFetcher statusCode]; +} + +- (GTLRQuery *)queryForRequestID:(NSString *)requestID { + id<GTLRQueryProtocol> queryObj = self.executingQuery; + if ([queryObj isBatchQuery]) { + GTLRBatchQuery *batch = (GTLRBatchQuery *)queryObj; + GTLRQuery *result = [batch queryForRequestID:requestID]; + return result; + } else { + GTLR_DEBUG_ASSERT(0, @"just use ticket.executingQuery"); + return nil; + } +} + +@end + +@implementation GTLRServiceExecutionParameters + +@synthesize maxRetryInterval = _maxRetryInterval, + retryEnabled = _retryEnabled, + retryBlock = _retryBlock, + shouldFetchNextPages = _shouldFetchNextPages, + objectClassResolver = _objectClassResolver, + testBlock = _testBlock, + ticketProperties = _ticketProperties, + uploadProgressBlock = _uploadProgressBlock, + callbackQueue = _callbackQueue; + +- (id)copyWithZone:(NSZone *)zone { + GTLRServiceExecutionParameters *newObject = [[self class] allocWithZone:zone]; + newObject.maxRetryInterval = self.maxRetryInterval; + newObject.retryEnabled = self.retryEnabled; + newObject.retryBlock = self.retryBlock; + newObject.shouldFetchNextPages = self.shouldFetchNextPages; + newObject.objectClassResolver = self.objectClassResolver; + newObject.testBlock = self.testBlock; + newObject.ticketProperties = self.ticketProperties; + newObject.uploadProgressBlock = self.uploadProgressBlock; + newObject.callbackQueue = self.callbackQueue; + return newObject; +} + +- (BOOL)hasParameters { + if (self.maxRetryInterval != nil) return YES; + if (self.retryEnabled != nil) return YES; + if (self.retryBlock) return YES; + if (self.shouldFetchNextPages != nil) return YES; + if (self.objectClassResolver) return YES; + if (self.testBlock) return YES; + if (self.ticketProperties) return YES; + if (self.uploadProgressBlock) return YES; + if (self.callbackQueue) return YES; + return NO; +} + +@end + + +@implementation GTLRResourceURLQuery + +@synthesize resourceURL = _resourceURL; + ++ (instancetype)queryWithResourceURL:(NSURL *)resourceURL + objectClass:(Class)objectClass { + GTLRResourceURLQuery *query = [[self alloc] initWithPathURITemplate:@"_usingGTLRResourceURLQuery_" + HTTPMethod:nil + pathParameterNames:nil]; + query.expectedObjectClass = objectClass; + query.resourceURL = resourceURL; + return query; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + GTLRResourceURLQuery *result = [super copyWithZone:zone]; + result->_resourceURL = self->_resourceURL; + return result; +} + +// TODO: description + +@end + +@implementation GTLRObjectCollectionImpl +@dynamic nextPageToken; +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.h b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2011 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. + */ + +// Uploading documentation: +// https://github.com/google/google-api-objectivec-client-for-rest/wiki#uploading-files + +#import <Foundation/Foundation.h> + +#import "GTLRDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Upload parameters are required for chunked-resumable or simple/multipart uploads. + * + * The MIME type and one source for data (@c NSData, file URL, or @c NSFileHandle) must + * be specified. + */ +@interface GTLRUploadParameters : NSObject <NSCopying> + +/** + * The type of media being uploaded. + */ +@property(atomic, copy, nullable) NSString *MIMEType; + +/** + * The media to be uploaded, represented as @c NSData. + */ +@property(atomic, retain, nullable) NSData *data; + +/** + * The URL for the local file to be uploaded. + */ +@property(atomic, retain, nullable) NSURL *fileURL; + +/** + * The media to be uploaded, represented as @c NSFileHandle. + * + * @note This property is provided for compatibility with older code. + * Uploading using @c fileURL is preferred over @c fileHandle + */ +@property(atomic, retain, nullable) NSFileHandle *fileHandle; + +/** + * Resuming an in-progress resumable, chunked upload is done with the upload location URL, + * and requires a file URL or file handle for uploading. + */ +@property(atomic, retain, nullable) NSURL *uploadLocationURL; + +/** + * Small uploads (for example, under 200K) can be done with a single multipart upload + * request. The upload body must be provided as NSData, not a file URL or file handle. + * + * Default value is NO. + */ +@property(atomic, assign) BOOL shouldUploadWithSingleRequest; + +/** + * Uploads may be done without a JSON body as metadata in the initial request. + * + * Default value is NO. + */ +@property(atomic, assign) BOOL shouldSendUploadOnly; + +/** + * Uploads may use a background session when uploading via GTMSessionUploadFetcher. + * Since background session fetches are slower than foreground fetches, this defaults + * to NO. + * + * It's reasonable for an application to set this to YES for a rare upload of a large file. + * + * Default value is NO. + * + * For more information about the hazards of background sessions, see the header comments for + * the GTMSessionFetcher useBackgroundSession property. + */ +@property(atomic, assign) BOOL useBackgroundSession; + +/** + * Constructor for uploading from @c NSData. + * + * @param data The data to uploaded. + * @param mimeType The media's type. + * + * @return The upload parameters object. + */ ++ (instancetype)uploadParametersWithData:(NSData *)data + MIMEType:(NSString *)mimeType; + +/** + * Constructor for uploading from a file URL. + * + * @param fileURL The file to upload. + * @param mimeType The media's type. + * + * @return The upload parameters object. + */ ++ (instancetype)uploadParametersWithFileURL:(NSURL *)fileURL + MIMEType:(NSString *)mimeType; + +/** + * Constructor for uploading from a file handle. + * + * @note This method is provided for compatibility with older code. To upload files, + * use a file URL. + */ ++ (instancetype)uploadParametersWithFileHandle:(NSFileHandle *)fileHandle + MIMEType:(NSString *)mimeType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.m b/Pods/GoogleAPIClientForREST/Source/Objects/GTLRUploadParameters.m @@ -0,0 +1,119 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#include <objc/runtime.h> + +#import "GTLRUploadParameters.h" + +@implementation GTLRUploadParameters + +@synthesize MIMEType = _MIMEType, + data = _data, + fileHandle = _fileHandle, + uploadLocationURL = _uploadLocationURL, + fileURL = _fileURL, + shouldUploadWithSingleRequest = _shouldUploadWithSingleRequest, + shouldSendUploadOnly = _shouldSendUploadOnly, + useBackgroundSession = _useBackgroundSession; + ++ (instancetype)uploadParametersWithData:(NSData *)data + MIMEType:(NSString *)mimeType { + GTLRUploadParameters *params = [[self alloc] init]; + params.data = data; + params.MIMEType = mimeType; + return params; +} + ++ (instancetype)uploadParametersWithFileHandle:(NSFileHandle *)fileHandle + MIMEType:(NSString *)mimeType { + GTLRUploadParameters *params = [[self alloc] init]; + params.fileHandle = fileHandle; + params.MIMEType = mimeType; + return params; +} + ++ (instancetype)uploadParametersWithFileURL:(NSURL *)fileURL + MIMEType:(NSString *)mimeType { + GTLRUploadParameters *params = [[self alloc] init]; + params.fileURL = fileURL; + params.MIMEType = mimeType; + return params; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLRUploadParameters *newParams = [[[self class] allocWithZone:zone] init]; + newParams.MIMEType = self.MIMEType; + newParams.data = self.data; + newParams.fileHandle = self.fileHandle; + newParams.fileURL = self.fileURL; + newParams.uploadLocationURL = self.uploadLocationURL; + newParams.shouldUploadWithSingleRequest = self.shouldUploadWithSingleRequest; + newParams.shouldSendUploadOnly = self.shouldSendUploadOnly; + newParams.useBackgroundSession = self.useBackgroundSession; + return newParams; +} + +#if DEBUG +- (NSString *)description { + NSMutableArray *array = [NSMutableArray array]; + NSString *str = [NSString stringWithFormat:@"MIMEType:%@", _MIMEType]; + [array addObject:str]; + + if (_data) { + str = [NSString stringWithFormat:@"data:%llu bytes", + (unsigned long long)_data.length]; + [array addObject:str]; + } + + if (_fileHandle) { + str = [NSString stringWithFormat:@"fileHandle:%@", _fileHandle]; + [array addObject:str]; + } + + if (_fileURL) { + str = [NSString stringWithFormat:@"file:%@", [_fileURL path]]; + [array addObject:str]; + } + + if (_uploadLocationURL) { + str = [NSString stringWithFormat:@"uploadLocation:%@", + [_uploadLocationURL absoluteString]]; + [array addObject:str]; + } + + if (_shouldSendUploadOnly) { + [array addObject:@"shouldSendUploadOnly"]; + } + + if (_shouldUploadWithSingleRequest) { + [array addObject:@"uploadWithSingleRequest"]; + } + + if (_useBackgroundSession) { + [array addObject:@"useBackgroundSession"]; + } + + NSString *descStr = [array componentsJoinedByString:@", "]; + str = [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, descStr]; + return str; +} +#endif // DEBUG + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.h b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2012 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +NSData * _Nullable GTLRDecodeBase64(NSString * _Nullable base64Str); +NSString * _Nullable GTLREncodeBase64(NSData * _Nullable data); + +// "Web-safe" encoding substitutes - and _ for + and / in the encoding table, +// per http://www.ietf.org/rfc/rfc4648.txt section 5. + +NSData * _Nullable GTLRDecodeWebSafeBase64(NSString * _Nullable base64Str); +NSString * _Nullable GTLREncodeWebSafeBase64(NSData * _Nullable data); + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.m b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRBase64.m @@ -0,0 +1,143 @@ +/* Copyright (c) 2012 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRBase64.h" + +// Based on Cyrus Najmabadi's elegent little encoder and decoder from +// http://www.cocoadev.com/index.pl?BaseSixtyFour + +static char gStandardEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static char gWebSafeEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +#pragma mark Encode + +static NSString *EncodeBase64StringCommon(NSData *data, const char *table) { + if (data == nil) return nil; + + const uint8_t* input = data.bytes; + NSUInteger length = data.length; + + NSUInteger bufferSize = ((length + 2) / 3) * 4; + NSMutableData* buffer = [NSMutableData dataWithLength:bufferSize]; + + int8_t *output = buffer.mutableBytes; + + for (NSUInteger i = 0; i < length; i += 3) { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) { + value <<= 8; + + if (j < length) { + value |= (0xFF & input[j]); + } + } + + NSInteger idx = (i / 3) * 4; + output[idx + 0] = table[(value >> 18) & 0x3F]; + output[idx + 1] = table[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; + } + + NSString *result = [[NSString alloc] initWithData:buffer + encoding:NSASCIIStringEncoding]; + return result; +} + +NSString *GTLREncodeBase64(NSData *data) { + return EncodeBase64StringCommon(data, gStandardEncodingTable); +} + +NSString *GTLREncodeWebSafeBase64(NSData *data) { + return EncodeBase64StringCommon(data, gWebSafeEncodingTable); +} + +#pragma mark Decode + +static void CreateDecodingTable(const char *encodingTable, + size_t encodingTableSize, char *decodingTable) { + memset(decodingTable, 0, 128); + for (unsigned int i = 0; i < encodingTableSize; i++) { + decodingTable[(unsigned int) encodingTable[i]] = (char)i; + } +} + +static NSData *DecodeBase64StringCommon(NSString *base64Str, + char *decodingTable) { + // The input string should be plain ASCII + const char *cString = [base64Str cStringUsingEncoding:NSASCIIStringEncoding]; + if (cString == nil) return nil; + + NSInteger inputLength = (NSInteger)strlen(cString); + if (inputLength % 4 != 0) return nil; + if (inputLength == 0) return [NSData data]; + + while (inputLength > 0 && cString[inputLength - 1] == '=') { + inputLength--; + } + + NSInteger outputLength = inputLength * 3 / 4; + NSMutableData* data = [NSMutableData dataWithLength:(NSUInteger)outputLength]; + uint8_t *output = data.mutableBytes; + + NSInteger inputPoint = 0; + NSInteger outputPoint = 0; + char *table = decodingTable; + + while (inputPoint < inputLength) { + int i0 = cString[inputPoint++]; + int i1 = cString[inputPoint++]; + int i2 = inputPoint < inputLength ? cString[inputPoint++] : 'A'; // 'A' will decode to \0 + int i3 = inputPoint < inputLength ? cString[inputPoint++] : 'A'; + + output[outputPoint++] = (uint8_t)((table[i0] << 2) | (table[i1] >> 4)); + if (outputPoint < outputLength) { + output[outputPoint++] = (uint8_t)(((table[i1] & 0xF) << 4) | (table[i2] >> 2)); + } + if (outputPoint < outputLength) { + output[outputPoint++] = (uint8_t)(((table[i2] & 0x3) << 6) | table[i3]); + } + } + + return data; +} + +NSData *GTLRDecodeBase64(NSString *base64Str) { + static char decodingTable[128]; + static BOOL hasInited = NO; + + if (!hasInited) { + CreateDecodingTable(gStandardEncodingTable, sizeof(gStandardEncodingTable), + decodingTable); + hasInited = YES; + } + return DecodeBase64StringCommon(base64Str, decodingTable); +} + +NSData *GTLRDecodeWebSafeBase64(NSString *base64Str) { + static char decodingTable[128]; + static BOOL hasInited = NO; + + if (!hasInited) { + CreateDecodingTable(gWebSafeEncodingTable, sizeof(gWebSafeEncodingTable), + decodingTable); + hasInited = YES; + } + return DecodeBase64StringCommon(base64Str, decodingTable); +} diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.h b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2011 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 <Foundation/Foundation.h> + +#import "GTLRDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +// Returns the version of the framework. Major and minor should +// match the bundle version in the Info.plist file. +// +// Pass NULL to ignore any of the parameters. + +void GTLRFrameworkVersion(NSUInteger * _Nullable major, + NSUInteger * _Nullable minor, + NSUInteger * _Nullable release); + +// Returns the version in @"a.b" or @"a.b.c" format +NSString *GTLRFrameworkVersionString(void); + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.m b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRFramework.m @@ -0,0 +1,44 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#include "GTLRFramework.h" + +void GTLRFrameworkVersion(NSUInteger* major, NSUInteger* minor, NSUInteger* release) { + // version 3.0.0 + if (major) *major = 3; + if (minor) *minor = 0; + if (release) *release = 0; +} + +NSString *GTLRFrameworkVersionString(void) { + NSUInteger major, minor, release; + NSString *libVersionString; + + GTLRFrameworkVersion(&major, &minor, &release); + + // most library releases will have a release value of zero + if (release != 0) { + libVersionString = [NSString stringWithFormat:@"%d.%d.%d", + (int)major, (int)minor, (int)release]; + } else { + libVersionString = [NSString stringWithFormat:@"%d.%d", + (int)major, (int)minor]; + } + return libVersionString; +} diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.h b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2010 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 <Foundation/Foundation.h> + +#ifndef SKIP_GTLR_DEFINES + #import "GTLRDefines.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +// +// URI Template +// +// http://tools.ietf.org/html/draft-gregorio-uritemplate-04 +// +// NOTE: This implemention is only a subset of the spec. It should be able +// to parse any tempate that matches the spec, but if the template makes use +// of a feature that is not supported, it will fail with an error. +// + +@interface GTLRURITemplate : NSObject + +// Process the template. If the template uses an unsupported feature, it will +// throw an exception to help catch that limitation. Currently unsupported +// feature is partial result modifiers (prefix/suffix). +// +// valueProvider should be anything that implements -objectForKey:. At the +// simplest level, this can be an NSDictionary. However, a custom class that +// implements valueForKey my be better for some uses. ++ (NSString *)expandTemplate:(NSString *)URITemplate + values:(NSDictionary *)valueProvider; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.m b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRURITemplate.m @@ -0,0 +1,511 @@ +/* Copyright (c) 2010 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRURITemplate.h" + +// Key constants for handling variables. +static NSString *const kVariable = @"variable"; // NSString +static NSString *const kExplode = @"explode"; // NSString +static NSString *const kPartial = @"partial"; // NSString +static NSString *const kPartialValue = @"partialValue"; // NSNumber + +// Help for passing the Expansion info in one shot. +struct ExpansionInfo { + // Constant for the whole expansion. + unichar expressionOperator; + __unsafe_unretained NSString *joiner; + BOOL allowReservedInEscape; + + // Update for each variable. + __unsafe_unretained NSString *explode; +}; + +// Helper just to shorten the lines when needed. +static NSString *UnescapeString(NSString *str) { + return [str stringByRemovingPercentEncoding]; +} + +static NSString *EscapeString(NSString *str, BOOL allowReserved) { + // The spec is a little hard to map onto the charsets, so force + // reserved bits in/out. + NSMutableCharacterSet *cs = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + NSString * const kReservedChars = @":/?#[]@!$&'()*+,;="; + if (allowReserved) { + [cs addCharactersInString:kReservedChars]; + } else { + [cs removeCharactersInString:kReservedChars]; + } + NSString *resultStr = [str stringByAddingPercentEncodingWithAllowedCharacters:cs]; + return resultStr; +} + +static NSString *StringFromNSNumber(NSNumber *rawValue) { + NSString *strValue; + // NSNumber doesn't expose a way to tell if it is holding a BOOL or something + // else. -[NSNumber objCType] for a BOOL is the same as @encoding(char), but + // in the 64bit runtine @encoding(BOOL) (or for "bool") won't match that as + // the 64bit runtime actually has a true boolean type. Instead we reply on + // checking if the numbers are the CFBoolean constants to force true/value + // values. + if ((rawValue == (NSNumber *)kCFBooleanTrue) || + (rawValue == (NSNumber *)kCFBooleanFalse)) { + strValue = (rawValue.boolValue ? @"true" : @"false"); + } else { + strValue = [rawValue stringValue]; + } + return strValue; +} + +@implementation GTLRURITemplate + +#pragma mark Internal Helpers + ++ (BOOL)parseExpression:(NSString *)expression + expressionOperator:(unichar*)outExpressionOperator + variables:(NSMutableArray **)outVariables + defaultValues:(NSMutableDictionary **)outDefaultValues { + + // Please see the spec for full details, but here are the basics: + // + // URI-Template = *( literals / expression ) + // expression = "{" [ operator ] variable-list "}" + // variable-list = varspec *( "," varspec ) + // varspec = varname [ modifier ] [ "=" default ] + // varname = varchar *( varchar / "." ) + // modifier = explode / partial + // explode = ( "*" / "+" ) + // partial = ( substring / remainder ) offset + // + // Examples: + // http://www.example.com/foo{?query,number} + // http://maps.com/mapper{?address*} + // http://directions.org/directions{?from+,to+} + // http://search.org/query{?terms+=none} + // + + // http://tools.ietf.org/html/draft-gregorio-uritemplate-04#section-2.2 + // Operator and op-reserve characters + static NSCharacterSet *operatorSet = nil; + // http://tools.ietf.org/html/draft-gregorio-uritemplate-04#section-2.4.1 + // Explode characters + static NSCharacterSet *explodeSet = nil; + // http://tools.ietf.org/html/draft-gregorio-uritemplate-04#section-2.4.2 + // Partial (prefix/subset) characters + static NSCharacterSet *partialSet = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + operatorSet = [NSCharacterSet characterSetWithCharactersInString:@"+./;?|!@"]; + explodeSet = [NSCharacterSet characterSetWithCharactersInString:@"*+"]; + partialSet = [NSCharacterSet characterSetWithCharactersInString:@":^"]; + }); + + // http://tools.ietf.org/html/draft-gregorio-uritemplate-04#section-3.3 + // Empty expression inlines the expression. + if (expression.length == 0) return NO; + + // Pull off any operator. + *outExpressionOperator = 0; + unichar firstChar = [expression characterAtIndex:0]; + if ([operatorSet characterIsMember:firstChar]) { + *outExpressionOperator = firstChar; + expression = [expression substringFromIndex:1]; + } + + if (expression.length == 0) return NO; + + // Need to find atleast one varspec for the expresssion to be considered + // valid. + BOOL gotAVarspec = NO; + + // Split the variable list. + NSArray *varspecs = [expression componentsSeparatedByString:@","]; + + // Extract the defaults, explodes and modifiers from the varspecs. + *outVariables = [NSMutableArray arrayWithCapacity:varspecs.count]; + for (__strong NSString *varspec in varspecs) { + NSString *defaultValue = nil; + + if (varspec.length == 0) continue; + + NSMutableDictionary *varInfo = + [NSMutableDictionary dictionaryWithCapacity:4]; + + // Check for a default (foo=bar). + NSRange range = [varspec rangeOfString:@"="]; + if (range.location != NSNotFound) { + defaultValue = + UnescapeString([varspec substringFromIndex:range.location + 1]); + varspec = [varspec substringToIndex:range.location]; + + if (varspec.length == 0) continue; + } + + // Check for explode (foo*). + NSUInteger lenLessOne = varspec.length - 1; + if ([explodeSet characterIsMember:[varspec characterAtIndex:lenLessOne]]) { + [varInfo setObject:[varspec substringFromIndex:lenLessOne] forKey:kExplode]; + varspec = [varspec substringToIndex:lenLessOne]; + if (varspec.length == 0) continue; + } else { + // Check for partial (prefix/suffix) (foo:12). + range = [varspec rangeOfCharacterFromSet:partialSet]; + if (range.location != NSNotFound) { + NSString *partialMode = [varspec substringWithRange:range]; + NSString *valueStr = [varspec substringFromIndex:range.location + 1]; + // If there wasn't a value for the partial, ignore it. + if (valueStr.length > 0) { + [varInfo setObject:partialMode forKey:kPartial]; + // TODO: Should validate valueStr is just a number... + [varInfo setObject:[NSNumber numberWithInteger:[valueStr integerValue]] + forKey:kPartialValue]; + } + varspec = [varspec substringToIndex:range.location]; + if (varspec.length == 0) continue; + } + } + + // Spec allows percent escaping in names, so undo that. + varspec = UnescapeString(varspec); + + // Save off the cleaned up variable name. + [varInfo setObject:varspec forKey:kVariable]; + [*outVariables addObject:varInfo]; + gotAVarspec = YES; + + // Now that the variable has been cleaned up, store its default. + if (defaultValue) { + if (*outDefaultValues == nil) { + *outDefaultValues = [NSMutableDictionary dictionary]; + } + [*outDefaultValues setObject:defaultValue forKey:varspec]; + } + } + // All done. + return gotAVarspec; +} + ++ (NSString *)expandVariables:(NSArray *)variables + expressionOperator:(unichar)expressionOperator + values:(NSDictionary *)valueProvider + defaultValues:(NSMutableDictionary *)defaultValues { + NSString *prefix = nil; + struct ExpansionInfo expansionInfo = { + .expressionOperator = expressionOperator, + .joiner = nil, + .allowReservedInEscape = NO, + .explode = nil, + }; + switch (expressionOperator) { + case 0: + expansionInfo.joiner = @","; + prefix = @""; + break; + case '+': + expansionInfo.joiner = @","; + prefix = @""; + // The reserved character are safe from escaping. + expansionInfo.allowReservedInEscape = YES; + break; + case '.': + expansionInfo.joiner = @"."; + prefix = @"."; + break; + case '/': + expansionInfo.joiner = @"/"; + prefix = @"/"; + break; + case ';': + expansionInfo.joiner = @";"; + prefix = @";"; + break; + case '?': + expansionInfo.joiner = @"&"; + prefix = @"?"; + break; + default: + [NSException raise:@"GTLRURITemplateUnsupported" + format:@"Unknown expression operator '%C'", expressionOperator]; + break; + } + + NSMutableArray *results = [NSMutableArray arrayWithCapacity:variables.count]; + + for (NSDictionary *varInfo in variables) { + NSString *variable = [varInfo objectForKey:kVariable]; + + expansionInfo.explode = [varInfo objectForKey:kExplode]; + // Look up the variable value. + id rawValue = [valueProvider objectForKey:variable]; + + // If the value is an empty array or dictionary, the default is still used. + if (([rawValue isKindOfClass:[NSArray class]] + || [rawValue isKindOfClass:[NSDictionary class]]) + && ((NSArray *)rawValue).count == 0) { + rawValue = nil; + } + + // Got nothing? Check defaults. + if (rawValue == nil) { + rawValue = [defaultValues objectForKey:variable]; + } + + // If we didn't get any value, on to the next thing. + if (!rawValue) { + continue; + } + + // Time do to the work... + NSString *result = nil; + if ([rawValue isKindOfClass:[NSString class]]) { + result = [self expandString:rawValue + variableName:variable + expansionInfo:&expansionInfo]; + } else if ([rawValue isKindOfClass:[NSNumber class]]) { + // Turn the number into a string and send it on its way. + NSString *strValue = StringFromNSNumber(rawValue); + result = [self expandString:strValue + variableName:variable + expansionInfo:&expansionInfo]; + } else if ([rawValue isKindOfClass:[NSArray class]]) { + result = [self expandArray:rawValue + variableName:variable + expansionInfo:&expansionInfo]; + } else if ([rawValue isKindOfClass:[NSDictionary class]]) { + result = [self expandDictionary:rawValue + variableName:variable + expansionInfo:&expansionInfo]; + } else { + [NSException raise:@"GTLRURITemplateUnsupported" + format:@"Variable returned unsupported type (%@)", + NSStringFromClass([rawValue class])]; + } + + // Did it generate anything? + if (result == nil) + continue; + + // Apply partial. + // Defaults should get partial applied? + // ( http://tools.ietf.org/html/draft-gregorio-uritemplate-04#section-2.5 ) + NSString *partial = [varInfo objectForKey:kPartial]; + if (partial.length > 0) { + [NSException raise:@"GTLRURITemplateUnsupported" + format:@"Unsupported partial on expansion %@", partial]; + } + + // Add the result + [results addObject:result]; + } + + // Join and add any needed prefix. + NSString *joinedResults = + [results componentsJoinedByString:expansionInfo.joiner]; + if ((prefix.length > 0) && (joinedResults.length > 0)) { + return [prefix stringByAppendingString:joinedResults]; + } + return joinedResults; +} + ++ (NSString *)expandString:(NSString *)valueStr + variableName:(NSString *)variableName + expansionInfo:(struct ExpansionInfo *)expansionInfo { + NSString *escapedValue = + EscapeString(valueStr, expansionInfo->allowReservedInEscape); + switch (expansionInfo->expressionOperator) { + case ';': + case '?': + if (valueStr.length > 0) { + return [NSString stringWithFormat:@"%@=%@", variableName, escapedValue]; + } + return variableName; + default: + return escapedValue; + } +} + ++ (NSString *)expandArray:(NSArray *)valueArray + variableName:(NSString *)variableName + expansionInfo:(struct ExpansionInfo *)expansionInfo { + NSMutableArray *results = [NSMutableArray arrayWithCapacity:valueArray.count]; + // When joining variable with value, use "var.val" except for 'path' and + // 'form' style expression, use 'var=val' then. + char variableValueJoiner = '.'; + unichar expressionOperator = expansionInfo->expressionOperator; + if ((expressionOperator == ';') || (expressionOperator == '?')) { + variableValueJoiner = '='; + } + // Loop over the values. + for (id rawValue in valueArray) { + NSString *value; + if ([rawValue isKindOfClass:[NSNumber class]]) { + value = StringFromNSNumber((id)rawValue); + } else if ([rawValue isKindOfClass:[NSString class]]) { + value = rawValue; + } else { + [NSException raise:@"GTLRURITemplateUnsupported" + format:@"Variable '%@' returned NSArray with unsupported type (%@), array: %@", + variableName, NSStringFromClass([rawValue class]), valueArray]; + } + // Escape it. + value = EscapeString(value, expansionInfo->allowReservedInEscape); + // Should variable names be used? + if ([expansionInfo->explode isEqual:@"+"]) { + value = [NSString stringWithFormat:@"%@%c%@", + variableName, variableValueJoiner, value]; + } + [results addObject:value]; + } + if (results.count > 0) { + // Use the default joiner unless there was no explode request, then a list + // always gets comma seperated. + NSString *joiner = expansionInfo->joiner; + if (expansionInfo->explode == nil) { + joiner = @","; + } + // Join the values. + NSString *joined = [results componentsJoinedByString:joiner]; + // 'form' style without an explode gets the variable name set to the + // joined list of values. + if ((expressionOperator == '?') && (expansionInfo->explode == nil)) { + return [NSString stringWithFormat:@"%@=%@", variableName, joined]; + } + return joined; + } + return nil; +} + ++ (NSString *)expandDictionary:(NSDictionary *)valueDict + variableName:(NSString *)variableName + expansionInfo:(struct ExpansionInfo *)expansionInfo { + NSMutableArray *results = [NSMutableArray arrayWithCapacity:valueDict.count]; + // When joining variable with value: + // - Default to the joiner... + // - No explode, always comma... + // - For 'path' and 'form' style expression, use 'var=val'. + NSString *keyValueJoiner = expansionInfo->joiner; + unichar expressionOperator = expansionInfo->expressionOperator; + if (expansionInfo->explode == nil) { + keyValueJoiner = @","; + } else if ((expressionOperator == ';') || (expressionOperator == '?')) { + keyValueJoiner = @"="; + } + // Loop over the sorted keys. + NSArray *sortedKeys = [valueDict.allKeys sortedArrayUsingSelector:@selector(compare:)]; + for (__strong NSString *key in sortedKeys) { + NSString *value = [valueDict objectForKey:key]; + // Escape them. + key = EscapeString(key, expansionInfo->allowReservedInEscape); + value = EscapeString(value, expansionInfo->allowReservedInEscape); + // Should variable names be used? + if ([expansionInfo->explode isEqual:@"+"]) { + key = [NSString stringWithFormat:@"%@.%@", variableName, key]; + } + if ((expressionOperator == '?' || expressionOperator == ';') + && (value.length == 0)) { + [results addObject:key]; + } else { + NSString *pair = [NSString stringWithFormat:@"%@%@%@", + key, keyValueJoiner, value]; + [results addObject:pair]; + } + } + if (results.count) { + // Use the default joiner unless there was no explode request, then a list + // always gets comma seperated. + NSString *joiner = expansionInfo->joiner; + if (expansionInfo->explode == nil) { + joiner = @","; + } + // Join the values. + NSString *joined = [results componentsJoinedByString:joiner]; + // 'form' style without an explode gets the variable name set to the + // joined list of values. + if ((expressionOperator == '?') && (expansionInfo->explode == nil)) { + return [NSString stringWithFormat:@"%@=%@", variableName, joined]; + } + return joined; + } + return nil; +} + +#pragma mark Public API + ++ (NSString *)expandTemplate:(NSString *)uriTemplate + values:(NSDictionary *)valueProvider { + NSMutableString *result = + [NSMutableString stringWithCapacity:uriTemplate.length]; + + NSScanner *scanner = [NSScanner scannerWithString:uriTemplate]; + [scanner setCharactersToBeSkipped:nil]; + + // Defaults have to live through the full evaluation, so if any are encoured + // they are reused throughout the expansion calls. + NSMutableDictionary *defaultValues = nil; + + // Pull out the expressions for processing. + while (![scanner isAtEnd]) { + NSString *skipped = nil; + // Find the next '{'. + if ([scanner scanUpToString:@"{" intoString:&skipped]) { + // Add anything before it to the result. + [result appendString:skipped]; + } + // Advance over the '{'. + [scanner scanString:@"{" intoString:nil]; + // Collect the expression. + NSString *expression = nil; + if ([scanner scanUpToString:@"}" intoString:&expression]) { + // Collect the trailing '}' on the expression. + BOOL hasTrailingBrace = [scanner scanString:@"}" intoString:nil]; + + // Parse the expression. + NSMutableArray *variables = nil; + unichar expressionOperator = 0; + if ([self parseExpression:expression + expressionOperator:&expressionOperator + variables:&variables + defaultValues:&defaultValues]) { + // Do the expansion. + NSString *substitution = [self expandVariables:variables + expressionOperator:expressionOperator + values:valueProvider + defaultValues:defaultValues]; + if (substitution) { + [result appendString:substitution]; + } + } else { + // Failed to parse, add the raw expression to the output. + if (hasTrailingBrace) { + [result appendFormat:@"{%@}", expression]; + } else { + [result appendFormat:@"{%@", expression]; + } + } + } else if (![scanner isAtEnd]) { + // Empty expression ('{}'). Copy over the opening brace and the trailing + // one will be copied by the next cycle of the loop. + [result appendString:@"{"]; + } + } + + return result; +} + +@end diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.h b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2011 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 <Foundation/Foundation.h> + +#ifndef SKIP_GTLR_DEFINES + #import "GTLRDefines.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +// Helper functions for implementing isEqual: +BOOL GTLR_AreEqualOrBothNil(id _Nullable obj1, id _Nullable obj2); +BOOL GTLR_AreBoolsEqual(BOOL b1, BOOL b2); + +// Helper to ensure a number is a number. +// +// The Google API servers will send numbers >53 bits as strings to avoid +// bugs in some JavaScript implementations. Work around this by catching +// the string and turning it back into a number. +NSNumber *GTLR_EnsureNSNumber(NSNumber *num); + +@interface GTLRUtilities : NSObject + +// Key-value coding searches in an array +// +// Utilities to get from an array objects having a known value (or nil) +// at a keyPath + ++ (NSArray *)objectsFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath; + ++ (nullable id)firstObjectFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.m b/Pods/GoogleAPIClientForREST/Source/Utilities/GTLRUtilities.m @@ -0,0 +1,117 @@ +/* Copyright (c) 2011 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. + */ + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import "GTLRUtilities.h" + +#include <objc/runtime.h> + +@implementation GTLRUtilities + +#pragma mark Key-Value Coding Searches in an Array + ++ (NSArray *)objectsFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath { + // Step through all entries, get the value from + // the key path, and see if it's equal to the + // desired value + NSMutableArray *results = [NSMutableArray array]; + + for(id obj in sourceArray) { + id val = [obj valueForKeyPath:keyPath]; + if (GTLR_AreEqualOrBothNil(val, desiredValue)) { + + // found a match; add it to the results array + [results addObject:obj]; + } + } + return results; +} + ++ (id)firstObjectFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath { + for (id obj in sourceArray) { + id val = [obj valueForKeyPath:keyPath]; + if (GTLR_AreEqualOrBothNil(val, desiredValue)) { + // found a match; return it + return obj; + } + } + return nil; +} + +#pragma mark Version helpers + +@end + +// isEqual: has the fatal flaw that it doesn't deal well with the receiver +// being nil. We'll use this utility instead. +BOOL GTLR_AreEqualOrBothNil(id obj1, id obj2) { + if (obj1 == obj2) { + return YES; + } + if (obj1 && obj2) { + BOOL areEqual = [(NSObject *)obj1 isEqual:obj2]; + return areEqual; + } + return NO; +} + +BOOL GTLR_AreBoolsEqual(BOOL b1, BOOL b2) { + // avoid comparison problems with boolean types by negating + // both booleans + return (!b1 == !b2); +} + +NSNumber *GTLR_EnsureNSNumber(NSNumber *num) { + // If the server returned a string object where we expect a number, try + // to make a number object. + if ([num isKindOfClass:[NSString class]]) { + NSNumber *newNum; + NSString *str = (NSString *)num; + if ([str rangeOfString:@"."].location != NSNotFound) { + // This is a floating-point number. + // Force the parser to use '.' as the decimal separator. + static NSLocale *usLocale = nil; + @synchronized([GTLRUtilities class]) { + if (usLocale == nil) { + usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + } + newNum = [NSDecimalNumber decimalNumberWithString:(NSString*)num + locale:(id)usLocale]; + } + } else { + // NSDecimalNumber +decimalNumberWithString:locale: + // does not correctly create an NSNumber for large values like + // 71100000000007780. + if ([str hasPrefix:@"-"]) { + newNum = @([str longLongValue]); + } else { + const char *utf8 = str.UTF8String; + unsigned long long ull = strtoull(utf8, NULL, 10); + newNum = @(ull); + } + } + if (newNum != nil) { + num = newNum; + } + } + return num; +} diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock @@ -19,6 +19,11 @@ PODS: - FirebaseCore (~> 5.2) - GoogleUtilities/Environment (~> 5.2) - GoogleUtilities/UserDefaults (~> 5.2) + - GoogleAPIClientForREST/Core (1.3.8): + - GTMSessionFetcher (>= 1.1.7) + - GoogleAPIClientForREST/Sheets (1.3.8): + - GoogleAPIClientForREST/Core + - GTMSessionFetcher (>= 1.1.7) - GoogleAppMeasurement (5.7.0): - GoogleUtilities/AppDelegateSwizzler (~> 5.2) - GoogleUtilities/MethodSwizzler (~> 5.2) @@ -43,6 +48,11 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (5.3.7): - GoogleUtilities/Logger + - GTMSessionFetcher (1.2.1): + - GTMSessionFetcher/Full (= 1.2.1) + - GTMSessionFetcher/Core (1.2.1) + - GTMSessionFetcher/Full (1.2.1): + - GTMSessionFetcher/Core (= 1.2.1) - nanopb (0.3.901): - nanopb/decode (= 0.3.901) - nanopb/encode (= 0.3.901) @@ -51,6 +61,7 @@ PODS: DEPENDENCIES: - Firebase/Core + - GoogleAPIClientForREST/Sheets SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -58,8 +69,10 @@ SPEC REPOS: - FirebaseAnalytics - FirebaseCore - FirebaseInstanceID + - GoogleAPIClientForREST - GoogleAppMeasurement - GoogleUtilities + - GTMSessionFetcher - nanopb SPEC CHECKSUMS: @@ -67,10 +80,12 @@ SPEC CHECKSUMS: FirebaseAnalytics: 23851fe602c872130a2c5c55040b302120346cc2 FirebaseCore: 52f851b30e11360f1e67cf04b1edfebf0a47a2d3 FirebaseInstanceID: bd6fc5a258884e206fd5c474ebe4f5b00e21770e + GoogleAPIClientForREST: 5447a194eae517986cafe6421a5330b80b820591 GoogleAppMeasurement: 6cf307834da065863f9faf4c0de0a936d81dd832 GoogleUtilities: 111a012f4c3a29c9e7c954c082fafd6ee3c999c0 + GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 -PODFILE CHECKSUM: 28902fd401dcd2075e3dd4d30d9e0c95e065ff7b +PODFILE CHECKSUM: 4852c02225a8d343844dd5ce2a4039dcb525ad3c COCOAPODS: 1.6.1 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj @@ -46,159 +46,217 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 0059C3A34002842E58E7A96B79ECD15C /* FIRInstanceIDCheckinPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = F6AA10866898B71F6A21627C4E1CD14D /* FIRInstanceIDCheckinPreferences.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 01D879EED839FAE13BE060638310A23E /* FIRComponentContainerInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 656A5D59CF895DF019000577CFBE409D /* FIRComponentContainerInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 02262A3E212C0EB9ACBC055C1FB08E3D /* FIRInstanceIDTokenFetchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = B754517F46C50C45E8EA76654D73524D /* FIRInstanceIDTokenFetchOperation.m */; }; - 033BDE177CE4158DBD1D93B86E0DA68C /* FirebaseInstanceID-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A4F2F1F710FE4FF3A1ACDAB7711391D /* FirebaseInstanceID-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0467A4AB857C1EA04F142878E6B5F7D7 /* FIRInstanceIDConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = DF49DEEE320B4A7DE9468F0EC4780D19 /* FIRInstanceIDConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 05F0BE6D01F6317AB56399068AA03408 /* GULNetworkURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E52F5018FDC1CBA40A1B2AF3701A7FA /* GULNetworkURLSession.m */; }; - 06716C4D82C54A67EE2FC8558AC001D3 /* FIRInstanceIDCheckinStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D51E316195287C40904DFCF52B1B8B3 /* FIRInstanceIDCheckinStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 06DE8D644B4500C4F25871F5817E9B30 /* GULUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = E097CA06B5E191A2ED986E28725A4B35 /* GULUserDefaults.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 09BA2B85B4D3A18F7D1E57BB80828BD9 /* GULNetworkConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = A6BA35651106407F616D7A57FE9D73D3 /* GULNetworkConstants.m */; }; + 0059C3A34002842E58E7A96B79ECD15C /* FIRInstanceIDCheckinPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 17CCF5036BBF759B3851A291325A58DD /* FIRInstanceIDCheckinPreferences.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 01D879EED839FAE13BE060638310A23E /* FIRComponentContainerInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 72A68E1354B8F68044AB4DD21E274D55 /* FIRComponentContainerInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 02262A3E212C0EB9ACBC055C1FB08E3D /* FIRInstanceIDTokenFetchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 6980A1AC965054D472E7A754E0433A29 /* FIRInstanceIDTokenFetchOperation.m */; }; + 033BDE177CE4158DBD1D93B86E0DA68C /* FirebaseInstanceID-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 87191E9BA56B22A703AD591A7963F5E3 /* FirebaseInstanceID-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0467A4AB857C1EA04F142878E6B5F7D7 /* FIRInstanceIDConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 347A040A0C9EF78AE6E2CC3305BA6394 /* FIRInstanceIDConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 05927EA99CFB71B69E8B3851D143526B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D669732E0BA0105016B0BFAF2C9EEE1 /* Security.framework */; }; + 05F0BE6D01F6317AB56399068AA03408 /* GULNetworkURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 6924954BD2F30993FB8230C4F0B6A5F5 /* GULNetworkURLSession.m */; }; + 06716C4D82C54A67EE2FC8558AC001D3 /* FIRInstanceIDCheckinStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BBF3A739B0E8360360E868AA65D007D /* FIRInstanceIDCheckinStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 06DE8D644B4500C4F25871F5817E9B30 /* GULUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CF16EA7D24BA0445B470F3104A38C2 /* GULUserDefaults.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 09BA2B85B4D3A18F7D1E57BB80828BD9 /* GULNetworkConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F65B52CC04DEB89684D01BB66C6E41 /* GULNetworkConstants.m */; }; + 09E4B4EBE4FBA72C30564F3E3DA2081F /* GTLRFramework.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F12DC4DF7E7F8A371738879B374FFF5 /* GTLRFramework.m */; }; 0A84CCA22F1C8173E6B37F2069950093 /* Pods-TeachersAssistantTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 176E21E234D1EF72C0869C40836ECBCC /* Pods-TeachersAssistantTests-dummy.m */; }; - 0AA1516C53AF76061CBAB2646F3AFFE8 /* pb.h in Headers */ = {isa = PBXBuildFile; fileRef = 933E7CB41F265BF8575D8E6571D4F745 /* pb.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0BE43A5E3B4F77F37B8343CAFB34785F /* FIRInstanceIDKeyPairUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = C6A1D7E752C1123A7CF007A1C2DD71F2 /* FIRInstanceIDKeyPairUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 11FC0A073774C034685F68FC4DBFAD71 /* FIRInstanceIDTokenStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64B7AC5ED456E5A4AB1B41E61E53FF71 /* FIRInstanceIDTokenStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 131C584CC42207AE38C9B02CCA93BC20 /* FIRLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = B04DBDA8123DF97BF5CEBC0B71C46B33 /* FIRLibrary.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 161DA7FD58D2B4BD6788ED1A1EC4D4D9 /* GoogleUtilities-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = AD20E02685107AFBC498147B840A6226 /* GoogleUtilities-dummy.m */; }; - 167EC0D6E6CA8F0A13B6CB28FCDFB2D3 /* FIRLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = D91D8C09AC5892352583E99233299DC4 /* FIRLogger.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 17C8EF1758E18D4BC2CDC094579C17AC /* FIRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CDFE0A10CBBC5A70389A8808D53982E /* FIRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1B76EA90F9022A327F46E8E13D367B81 /* GULNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BC59F9C9C92C79BD213359E59BC0083 /* GULNetwork.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 1C13102930FBAE48D3508BE1166A7254 /* FIRVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = EA887FCB820DCD0CB25B1DBEAC519286 /* FIRVersion.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 1DBEB4345285DDD0576C9205C1607F45 /* FIRComponentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 6158927EA607D8FEFA5317569E679441 /* FIRComponentType.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 2057D78059437EADF8E509FB3A4E3463 /* FIRLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = AE3708AB69977852B8EC58F245468A62 /* FIRLogger.m */; }; - 20A087E51101B8CB92AD91F7248A5AF3 /* pb_decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 63F5C9FB083CECFE9AD2689CAFED23FE /* pb_decode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 20A5A8E9A67B9FF611833437B7C5EA4B /* FIRAppAssociationRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CE321B66335679D34B98DD90B325217 /* FIRAppAssociationRegistration.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 20F3EC5F96CECCC418924F80B181053B /* nanopb-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DD326385780370B960613290BF8826DE /* nanopb-dummy.m */; }; - 2120FC11F12792CCF30112ABF6AECDC2 /* FIRLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D0D5C4A8C0DE64D2AC9920D785D32E /* FIRLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 225CD805988CB60469B5BF834F3D9CF2 /* FIRInstanceIDTokenInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4242EFC1874D23623622538D952F8B5B /* FIRInstanceIDTokenInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 22C2EC4924A727034D60B942E9FB3B76 /* FIRInstanceIDCheckinService.h in Headers */ = {isa = PBXBuildFile; fileRef = 08B3E50C44804158B340813635E6BC09 /* FIRInstanceIDCheckinService.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 2390AF73CF77000D9FFA4F7E71F87709 /* FIRInstanceIDKeyPairUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7397308853202DF815AD5A1CD1C07E45 /* FIRInstanceIDKeyPairUtilities.m */; }; - 250394D1E8BA232672EDB0720E269F26 /* GULAppEnvironmentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 923510B47157D70AE61469BD0DB53003 /* GULAppEnvironmentUtil.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 28FD68E488D4EB3F967E24EB321A3D8C /* FIRInstanceIDAuthKeyChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DED144D39D4BC1B72D7492161128B2E /* FIRInstanceIDAuthKeyChain.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 29D6BF1387573FF76079F4370615F42F /* FIRDependency.h in Headers */ = {isa = PBXBuildFile; fileRef = 182BE2FC262ED73F2E01FFAF232D570E /* FIRDependency.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 2C104E24C5AD39EAEA527722426513D0 /* FIRInstanceIDConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = B6677173DD856010DD44F240491627F5 /* FIRInstanceIDConstants.m */; }; - 2E4BF72932067C69FF51DB6AC0790798 /* GULUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 975AEB856E53CB32B52EE02D1BC7D00C /* GULUserDefaults.m */; }; - 2F4F0E5F03828E47ACDB08F50C32750A /* pb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 10C2190ED503628386EB3FA0ACBBBC13 /* pb_common.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3114C26A42681313B7F292DEFEA4AB23 /* FIRAnalyticsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C905C3155AA4CAC5E7249613453764B /* FIRAnalyticsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3186A8F692C6DCA34411EBCED965DA5C /* GULOriginalIMPConvenienceMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B956219AB913B55F60DC376E29FADC1 /* GULOriginalIMPConvenienceMacros.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 319C57F799237E5E10948965A2A94843 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - 3358337525AF2964298F3C2457BAA271 /* FIRInstanceIDAPNSInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D314385673422BB83DB1058A7355DDF3 /* FIRInstanceIDAPNSInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 350A5A886E6A58D8CDCDD4253BC38BDC /* nanopb-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E1E0BB483A5ED582BCF730EFC79223 /* nanopb-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 35D665B3D90C6BE89DEC067A5ED94F57 /* GULNetworkConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C5329F040451FE49D3DDAB4C1ED7F4B0 /* GULNetworkConstants.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 376B561BE4A9CF76B656848C439B8DE7 /* NSError+FIRInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = CE46A1737F9960DE2A5BEABE471AAAB7 /* NSError+FIRInstanceID.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 3B68F1157F767E0A567E3342BE503D66 /* FIRErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = EB1180F966E47C1728EA6314393950BE /* FIRErrors.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 3BB343D149E94DCA9736241B605534AA /* GULAppEnvironmentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F822B32EED40D27314DFE21C5EDDA6A /* GULAppEnvironmentUtil.m */; }; - 3C7B678AE30C0CFDF2F6230C4182405B /* FIRInstanceID+Testing.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D4B5C9B43DB2526EF6BF314E82C38F1 /* FIRInstanceID+Testing.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 3E3765FEDEFB49732071DF7CCFDA4F18 /* NSError+FIRInstanceID.m in Sources */ = {isa = PBXBuildFile; fileRef = B70A1D762675BEC99064CE31C0656656 /* NSError+FIRInstanceID.m */; }; - 3F5F0473B00C277797303FA939E6DBAB /* GULMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DE9D4D09D58B6CC296CC31B28C71A68 /* GULMutableDictionary.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 40F36D2E46BA807F50F3E49F1A3F14A0 /* FirebaseInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = 3922B447A4A2A874121C267E40012CD2 /* FirebaseInstanceID.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 43A96F3C7695AE0E52B91FFD50FFB008 /* FIRInstanceIDStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 69D06FAE028081AF31F2CC18622AEB9B /* FIRInstanceIDStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 45BF0F342DD18D393CF71EA76BCAF831 /* FIRInstanceIDKeyPair.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD50293995AD747738A754E700980D2 /* FIRInstanceIDKeyPair.m */; }; - 45E1670E670793EE76B9327441B0BC32 /* Pods-TeachersAssistant-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 74104373A68924B948E2FD66656DDF04 /* Pods-TeachersAssistant-dummy.m */; }; - 46FFA8D318E8298B80CFF968BD58A1DE /* FIRInstanceIDKeyPairStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 0197914611D5254348329F289DE67F28 /* FIRInstanceIDKeyPairStore.m */; }; - 49EA6B4459F2B9C1CE0E1DCAE261FDEF /* GULNetworkURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 16F8B483F73D5BC309CCBD721A6B71C0 /* GULNetworkURLSession.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 4AEAF2548FD03171132E796282A5A71A /* FIRBundleUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = CDDFD3D1A057D712D16DB6E8B4D63C68 /* FIRBundleUtil.m */; }; - 4BA3FA7832147EC2B3EE59FD01CC2CAE /* GULReachabilityChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C80B622EEB6267EBB46092F5459CA2 /* GULReachabilityChecker.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 4CD69ED3DFC0C9AE77A59B70AF7FE036 /* pb_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = A183E0073F3551A0DFFBAD1EE4650667 /* pb_encode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; }; - 4DB3785D5FF90EE291AD9E6888678568 /* FIRErrorCode.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F1262F3DD318730F73C2EE5DE40EF8 /* FIRErrorCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 4DF83A76C578938051AFFAC51A0E1471 /* pb_common.c in Sources */ = {isa = PBXBuildFile; fileRef = AE892269DC64E59580E85799336B0E3F /* pb_common.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc -fno-objc-arc"; }; }; - 526B347701CC61AA8D42C5004F5DACDD /* FIRInstanceIDTokenOperation+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 16D15E2B7F18A663F0CB385DB75F0DC1 /* FIRInstanceIDTokenOperation+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 531D80A0B9967E698C050D4621953516 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - 56BAB83EAE402801FA7BA3911CCF67D7 /* FIRInstanceIDAuthKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 16ABBA3AED50ADB109B70827858F46FB /* FIRInstanceIDAuthKeyChain.m */; }; - 57600D38E2FDA193B83876BC5357B486 /* FIRInstanceIDKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F162645BE355033F19D49D40BEA124E /* FIRInstanceIDKeyPair.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5803D830BEAB4B1992945388A716537D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - 59CEC609B2677DAC48C94403C96404CD /* FIRInstanceIDKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FE8EA3EF498147F2DEA6E1B70F70E9 /* FIRInstanceIDKeychain.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5A84786CE0B0F1A1F8090E765B046C4D /* FIRAppInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C91CF94C6994C814D43E3FCD79444A0 /* FIRAppInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 5AE38B0A395E28D92DF14A261A9EBE45 /* FIRInstanceIDKeyPairStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 034660AA71C364B6CE19A7775ACBFF3F /* FIRInstanceIDKeyPairStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5B469132FE6F3D6BD781A223B88A5F41 /* FIROptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D1936355E3B3A5466062133CD40EE0E /* FIROptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5BA1BBD448693996005C28B6F23F9860 /* FIRInstanceIDCheckinPreferences+Internal.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F3D9F0F4CB406856DE551E00F4BA94 /* FIRInstanceIDCheckinPreferences+Internal.m */; }; - 5DE36E758B1620E0A005E2038548BFE8 /* FIRInstanceIDTokenOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BAC6A2CCC656481599672B6E990A5BD /* FIRInstanceIDTokenOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5E638B27DC537F86FE14FD7E312A350E /* FIRVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A7C56749F7DFBFB68E361532C23A68A /* FIRVersion.m */; }; - 5F2485A11C7E0A6818B2FB30C4B2146D /* FIRInstanceIDStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 600DA0E155B6B8E031A9B0DB8F58EC5F /* FIRInstanceIDStore.m */; }; - 60D21973F190744E3DC7BE8B088679C3 /* FirebaseInstanceID-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EA88B41DC30B62C05F0F2C3DBDDB74BE /* FirebaseInstanceID-dummy.m */; }; - 61B23F7031A1C5919E7338E490C116E7 /* FIRInstanceIDKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = DEA669FCEC4147F00A475E2483BF28F6 /* FIRInstanceIDKeychain.m */; }; - 62D28905E5357811247084EA63BBF4DA /* GULNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 95A030E997930B42AFCBB55C290C6DC7 /* GULNetwork.m */; }; - 63E86A46BDB0362808FB9BA64DE35DA5 /* FIRInstanceIDDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 30CA044A7E9853B9620403801FFD468A /* FIRInstanceIDDefines.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 6AA5596E1B32B6664794B84C8D53BB3C /* GULMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 01168DD1A418F79F4BAAA97A8902A73A /* GULMutableDictionary.m */; }; + 0AA1516C53AF76061CBAB2646F3AFFE8 /* pb.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B79195C4C8B553F57B3D3535D34328 /* pb.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0BE43A5E3B4F77F37B8343CAFB34785F /* FIRInstanceIDKeyPairUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 500B9CE6A2B5ECC69A261EE855D14BDC /* FIRInstanceIDKeyPairUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0DC5A104F5C14D11C8CA9F2EBEB41725 /* GTLRUploadParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = B5047997309AE128643CDACF85D5C260 /* GTLRUploadParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 102658B33FEC7C008C1C8229F40351F9 /* GTMSessionFetcherService.h in Headers */ = {isa = PBXBuildFile; fileRef = CF8C86C20862941343D914D49B5DE74C /* GTMSessionFetcherService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 11FC0A073774C034685F68FC4DBFAD71 /* FIRInstanceIDTokenStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 85599DEC750671534D9582042B8D286B /* FIRInstanceIDTokenStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 131C584CC42207AE38C9B02CCA93BC20 /* FIRLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 374883FE51263B2BD861B86677BF5ECA /* FIRLibrary.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 161DA7FD58D2B4BD6788ED1A1EC4D4D9 /* GoogleUtilities-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BF158F186A2E83AA478BC8C6ECB96907 /* GoogleUtilities-dummy.m */; }; + 167EC0D6E6CA8F0A13B6CB28FCDFB2D3 /* FIRLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DE4621B5BF4A7F747C7A66937E79855 /* FIRLogger.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 1752930EA1F0BD6E9408C8298F178491 /* GTMMIMEDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 4370B2DFACB00E7DEC0868A68AA87DBD /* GTMMIMEDocument.m */; }; + 17C8EF1758E18D4BC2CDC094579C17AC /* FIRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C940E5390B103F4CE2892BFFBC987A /* FIRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1B76EA90F9022A327F46E8E13D367B81 /* GULNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = A2672AB4665EB890F1F2E052DC068B0D /* GULNetwork.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 1C13102930FBAE48D3508BE1166A7254 /* FIRVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 357B0ECED3D296D4A9739C22E28FF76D /* FIRVersion.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 1DBEB4345285DDD0576C9205C1607F45 /* FIRComponentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B7B16A2741E588921CA76370A6E0C6 /* FIRComponentType.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 2057D78059437EADF8E509FB3A4E3463 /* FIRLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 745F466B0E0CFD0057D71D440A6D7D4D /* FIRLogger.m */; }; + 20A087E51101B8CB92AD91F7248A5AF3 /* pb_decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 734E5DCCCE857FFA34745EB5C2D6C5EA /* pb_decode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 20A5A8E9A67B9FF611833437B7C5EA4B /* FIRAppAssociationRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = A62C33CAAD941D27993CFB927857A476 /* FIRAppAssociationRegistration.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 20F3EC5F96CECCC418924F80B181053B /* nanopb-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 41F647164C5CA7901E4BFE7741255A55 /* nanopb-dummy.m */; }; + 2120FC11F12792CCF30112ABF6AECDC2 /* FIRLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 20BB64A0D3C190E922B42A7DABDF4351 /* FIRLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 225CD805988CB60469B5BF834F3D9CF2 /* FIRInstanceIDTokenInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D96D27B23ED456F3F91185B0591AA335 /* FIRInstanceIDTokenInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 22C2EC4924A727034D60B942E9FB3B76 /* FIRInstanceIDCheckinService.h in Headers */ = {isa = PBXBuildFile; fileRef = C509EB270A3FE03A71169745013D5768 /* FIRInstanceIDCheckinService.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2300C13FB133313D32DFFB0D57C25ECC /* GTMSessionUploadFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = AD9455B83D23BDB4915ED6A130D1C8C4 /* GTMSessionUploadFetcher.m */; }; + 2390AF73CF77000D9FFA4F7E71F87709 /* FIRInstanceIDKeyPairUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3895CC74E7806861C8DB5D96CD95C007 /* FIRInstanceIDKeyPairUtilities.m */; }; + 250394D1E8BA232672EDB0720E269F26 /* GULAppEnvironmentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C7078467D0A33F3EBEFE639202A278A /* GULAppEnvironmentUtil.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 26FE54055CE40CA8B7B8556919C21149 /* GTLRDateTime.m in Sources */ = {isa = PBXBuildFile; fileRef = F22DC88B182B57DE12E328AAEF3F023C /* GTLRDateTime.m */; }; + 28FD68E488D4EB3F967E24EB321A3D8C /* FIRInstanceIDAuthKeyChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 3E8692905552E367BFE89057E3905DB9 /* FIRInstanceIDAuthKeyChain.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 29D6BF1387573FF76079F4370615F42F /* FIRDependency.h in Headers */ = {isa = PBXBuildFile; fileRef = 2702574391D7BC669EE01BBCB336A64C /* FIRDependency.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 2C104E24C5AD39EAEA527722426513D0 /* FIRInstanceIDConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 2897AC5168624488338884B7C36B5865 /* FIRInstanceIDConstants.m */; }; + 2C992FD410CB75581BF435EA7A66C903 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 2E4BF72932067C69FF51DB6AC0790798 /* GULUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AEFDB9DD19C098BAA39381962D72398 /* GULUserDefaults.m */; }; + 2ECE32250F9379832BA31C5788E02EBC /* GTLRSheetsService.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DEDD6F11A6592A96F72E6E90EC1FD73 /* GTLRSheetsService.m */; }; + 2F4F0E5F03828E47ACDB08F50C32750A /* pb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = E63ABD34201B77F662B8D9B9DA44CDC6 /* pb_common.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3114C26A42681313B7F292DEFEA4AB23 /* FIRAnalyticsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F047222C8056B6A3AD7E289C9EE21D28 /* FIRAnalyticsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3186A8F692C6DCA34411EBCED965DA5C /* GULOriginalIMPConvenienceMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B6E82FA3B095C371861788955184AAA /* GULOriginalIMPConvenienceMacros.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 319C57F799237E5E10948965A2A94843 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 3358337525AF2964298F3C2457BAA271 /* FIRInstanceIDAPNSInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 1331D13DD4DE386BCEDBC001F6B10B28 /* FIRInstanceIDAPNSInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 350A5A886E6A58D8CDCDD4253BC38BDC /* nanopb-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C00A42C8667CADDFB41282D26A16227 /* nanopb-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35D665B3D90C6BE89DEC067A5ED94F57 /* GULNetworkConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 2060C1F4AD9CA43E0B2EA2B28F55A284 /* GULNetworkConstants.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 376B561BE4A9CF76B656848C439B8DE7 /* NSError+FIRInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = 54C93ABAE1D2705DC66F2D99C41F09C5 /* NSError+FIRInstanceID.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 38AE6B968DC0DE94A749503C4FF43E51 /* GTMMIMEDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 88278CB0E802158640AF8B0E24FAD79A /* GTMMIMEDocument.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3B68F1157F767E0A567E3342BE503D66 /* FIRErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CA5A0BB0A7F36E8F7189D91C8B29728 /* FIRErrors.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3BB343D149E94DCA9736241B605534AA /* GULAppEnvironmentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 294D8D5384102F5EC8F36C99BD40B553 /* GULAppEnvironmentUtil.m */; }; + 3C650CC5DFF8B2369B0326CF9EFA3B74 /* GTLRFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 217305076F6C446D80ECEE650D6FF336 /* GTLRFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3C7B678AE30C0CFDF2F6230C4182405B /* FIRInstanceID+Testing.h in Headers */ = {isa = PBXBuildFile; fileRef = CDECB42CD69FE8C441A88EB09E5E41B1 /* FIRInstanceID+Testing.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3E3765FEDEFB49732071DF7CCFDA4F18 /* NSError+FIRInstanceID.m in Sources */ = {isa = PBXBuildFile; fileRef = FB866184F9DB8C552543637E0980B4FA /* NSError+FIRInstanceID.m */; }; + 3F5F0473B00C277797303FA939E6DBAB /* GULMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 83537337655F0E50082CD7E2603EFCCA /* GULMutableDictionary.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 40F36D2E46BA807F50F3E49F1A3F14A0 /* FirebaseInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = 15D8C40F17E82EDAAA2FD2FEE97387AD /* FirebaseInstanceID.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4113F1C1982CB9F61B0877D8E2A3C982 /* GTMGatherInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC9D2775C1E7A3C43A2D9FB68FD6140 /* GTMGatherInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 43A96F3C7695AE0E52B91FFD50FFB008 /* FIRInstanceIDStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 559A7AF75C7825A882D221F994EDE0FE /* FIRInstanceIDStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 45BF0F342DD18D393CF71EA76BCAF831 /* FIRInstanceIDKeyPair.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2D1B3717FA3F20CC83DA53908482 /* FIRInstanceIDKeyPair.m */; }; + 46FFA8D318E8298B80CFF968BD58A1DE /* FIRInstanceIDKeyPairStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CA82E29BA583AD0A52D7A37558E2F9 /* FIRInstanceIDKeyPairStore.m */; }; + 49EA6B4459F2B9C1CE0E1DCAE261FDEF /* GULNetworkURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CEB8453EFC3EBB8CC7F816596220DD /* GULNetworkURLSession.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4AEAF2548FD03171132E796282A5A71A /* FIRBundleUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C99E6C3748A149B14ABA4CF063DD4F52 /* FIRBundleUtil.m */; }; + 4BA3FA7832147EC2B3EE59FD01CC2CAE /* GULReachabilityChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CCAEF0B1870F052EDE7D124EE35E5F2 /* GULReachabilityChecker.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4CD69ED3DFC0C9AE77A59B70AF7FE036 /* pb_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = 6C217B0FC3899164615FD61D788E7FBA /* pb_encode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; }; + 4DB3785D5FF90EE291AD9E6888678568 /* FIRErrorCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 637245938FD074F3B9AE443C80D0A34E /* FIRErrorCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4DF83A76C578938051AFFAC51A0E1471 /* pb_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 3CAC70B7C1A8906857FA3FC99C1BD4EC /* pb_common.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc -fno-objc-arc"; }; }; + 4F6EF06D616D2AEDAEAC81ECDE2C488E /* GTLRUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = B999E5AD02D47AC06D46363EEC291140 /* GTLRUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50D319FF764331FCABB481C1A7D835E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 526B347701CC61AA8D42C5004F5DACDD /* FIRInstanceIDTokenOperation+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 29BA6B4B3A27AAF40CE91D7F460B4C09 /* FIRInstanceIDTokenOperation+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 529E198E8565F59B3BE1364522AAEC87 /* GTLRSheetsService.h in Headers */ = {isa = PBXBuildFile; fileRef = EEF2A1FD563D62873F1FE536E3F72F91 /* GTLRSheetsService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 531D80A0B9967E698C050D4621953516 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 55E0ADCA640BB3322559794AED57F451 /* GTMSessionFetcherLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA616F95FA0F7EB03F1582C09FCD657 /* GTMSessionFetcherLogging.m */; }; + 564FB83D43683D79E3DF91EC972184D8 /* GTLRObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFA1AB5522EACA4F67A002CF883955E /* GTLRObject.m */; }; + 56BAB83EAE402801FA7BA3911CCF67D7 /* FIRInstanceIDAuthKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F06F1782AC0D2EF8E359A25024009A7 /* FIRInstanceIDAuthKeyChain.m */; }; + 57600D38E2FDA193B83876BC5357B486 /* FIRInstanceIDKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = E9E92D24EC1A5967CA10893102360920 /* FIRInstanceIDKeyPair.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 57C2471A37561E07AE381E7E346F77B3 /* GTLRSheetsObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = BA38906194CC3F29E0F1F97D1A889161 /* GTLRSheetsObjects.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57C854E399EE2DDEA121CD54EE49CB07 /* Pods-TeachersAssistant-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DFABC77294AE1B3C79891E913990217 /* Pods-TeachersAssistant-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5803D830BEAB4B1992945388A716537D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 5992ECFCB7E99F063A2D36AE73F44D60 /* GTMSessionFetcherService.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAFAC2A9A529CD28ED16BCF13AECE4 /* GTMSessionFetcherService.m */; }; + 59CEC609B2677DAC48C94403C96404CD /* FIRInstanceIDKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 7943A53065BB0DF789C3CDEEC1004CD9 /* FIRInstanceIDKeychain.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5A84786CE0B0F1A1F8090E765B046C4D /* FIRAppInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 042BE6629EC9A2E9F87D17CED7AACB6A /* FIRAppInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 5AE38B0A395E28D92DF14A261A9EBE45 /* FIRInstanceIDKeyPairStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 290A38F9B7BEF687F3DBB3C3255CC233 /* FIRInstanceIDKeyPairStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5B469132FE6F3D6BD781A223B88A5F41 /* FIROptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C62C59F592BBF3BA1637FDA859BE9A6 /* FIROptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5BA1BBD448693996005C28B6F23F9860 /* FIRInstanceIDCheckinPreferences+Internal.m in Sources */ = {isa = PBXBuildFile; fileRef = DB676188CF095CD4CF367F5EBC478EA7 /* FIRInstanceIDCheckinPreferences+Internal.m */; }; + 5D2B3289AB0C9082A36E106B6FC1A1C9 /* GTLRErrorObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4673860D113355AAD052F21D1D4399F7 /* GTLRErrorObject.m */; }; + 5DE36E758B1620E0A005E2038548BFE8 /* FIRInstanceIDTokenOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 39DE45AADB09BA42C0F26F4A09E7BBEE /* FIRInstanceIDTokenOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5E638B27DC537F86FE14FD7E312A350E /* FIRVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 321D5E4C31CED38A5BE1C3397595E7F5 /* FIRVersion.m */; }; + 5F2485A11C7E0A6818B2FB30C4B2146D /* FIRInstanceIDStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 61C9D32696A6761D07B73E8E6A1B0603 /* FIRInstanceIDStore.m */; }; + 60070AAFBCB471BDF2A5903E309BF939 /* GTMSessionFetcher-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 09CF74BB4F08D9BA981B0FCB07128326 /* GTMSessionFetcher-dummy.m */; }; + 60D21973F190744E3DC7BE8B088679C3 /* FirebaseInstanceID-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EDD142F7B8EDBDDDDB5BF548CD9DED8 /* FirebaseInstanceID-dummy.m */; }; + 61B23F7031A1C5919E7338E490C116E7 /* FIRInstanceIDKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4F8A5BE53C6A071CEA011E4022665 /* FIRInstanceIDKeychain.m */; }; + 62D28905E5357811247084EA63BBF4DA /* GULNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8C5B5B378C757D7F3D05B7BBF50F99 /* GULNetwork.m */; }; + 63CBC280596E3A453DF91743EBBC898A /* GTLRDuration.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CA56DE4B76F14F33BDA8853272D8366 /* GTLRDuration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 63E86A46BDB0362808FB9BA64DE35DA5 /* FIRInstanceIDDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BC4FA64AE2079B4E811849A1860A4A6 /* FIRInstanceIDDefines.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 656F3D54ACD64C547F9594F124FA2D5E /* GTLRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E92A8BA31159E11D52BAF17FD3D11F2 /* GTLRUtilities.m */; }; + 689CA019D0B329049875601AD99A0C04 /* GTLRSheets.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E9773C37D034C604399ABF8364696FC /* GTLRSheets.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69F46ADB15D518B3F0323377AFEE974E /* GTLRBatchQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 74EF9C835171D35F298E15ACA4B38506 /* GTLRBatchQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6AA5596E1B32B6664794B84C8D53BB3C /* GULMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 1201678D8F7875046192E6EF9294A1C4 /* GULMutableDictionary.m */; }; + 6B80256F23FA1918E977CEDE91CE3697 /* GTLRDuration.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AF2AA89291E5567866849C26499D /* GTLRDuration.m */; }; 6C571A236A1EBAA0111698DE1FC4D0A4 /* Pods-TeachersAssistantTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 029A66B7819D4FD2C4DD1328C5488419 /* Pods-TeachersAssistantTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6C8C3F762C321C7FCF5D7A91E0CBD4B1 /* GULNetworkLoggerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = A0802C848647300E14D9CEAF6F586E3D /* GULNetworkLoggerProtocol.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 6D2D67FFD4FD3CA65BF5C77DA6EBA08B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - 6EC07394A547C7982468ED9069AAB53B /* FIRInstanceIDVersionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0A7EDEA8C34882ED9F21855FFD6B54 /* FIRInstanceIDVersionUtilities.m */; }; - 6FD0FBAB532F0C93B7B8909FC99BFE97 /* FIRInstanceIDBackupExcludedPlist.h in Headers */ = {isa = PBXBuildFile; fileRef = A19E92882D90BF25AF701FA415D5168C /* FIRInstanceIDBackupExcludedPlist.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 723B1DEC2C8098017A02DF898B87E8D0 /* GULLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A95F0619B59607B38D60DFC6A5815C4 /* GULLogger.m */; }; - 76478F8839B3E900D460C0DE15D21F53 /* FIRErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6F1873C3979691F097FF8841EBBD85 /* FIRErrors.m */; }; - 764B2AE2F2B17F964F6437AE468C0F15 /* FIRInstanceIDTokenOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = B56AAB2892E7D78A490ACD5C6E90A87F /* FIRInstanceIDTokenOperation.m */; }; - 7B4645279AD67C809174C1B58AD6F915 /* FIRInstanceIDTokenStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B6E9D37CB97B82EC30EDA29C24BFA28 /* FIRInstanceIDTokenStore.m */; }; - 7D49364D45BC35C596753015EED2158D /* FIRInstanceIDTokenManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BDDCC43E635D0901D6438F620AB5C3 /* FIRInstanceIDTokenManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7E169F6567DADEB4C0269E64A51E6DA4 /* GULReachabilityMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 06191F1C90B569E50C136B8829E2546A /* GULReachabilityMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 7F6426897180E49EE98F3A6CE8F24D58 /* FIRInstanceIDLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 87278A4E974DA21F7BA3EE74DE03DADE /* FIRInstanceIDLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7FB4AACD60131CB28B62F5EDB531AAA4 /* FIRInstanceIDCheckinPreferences+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1A828E33A41A2284217BC01E0085B8 /* FIRInstanceIDCheckinPreferences+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7FE7DA15DFBB03C6F320F4137F0284ED /* GULSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CFE5D0397A33F3BB0D159D063A0730D /* GULSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 8343914CF540F43454F0A80F319961E8 /* FIRAnalyticsConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AF42147C59B306D29A7697CF513F604 /* FIRAnalyticsConfiguration+Internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 854834B0636DAEB384EAC5BBFA00993A /* FIRComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 09461C56109A35828704974C4C9E9614 /* FIRComponent.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 876F2110FD88F97EDECF6652D7193836 /* FIRIMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = E1211DD77B6981F4FE4EC8D8CA0DD26C /* FIRIMessageCode.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 88B58642B5770D7313961DB5AAC02C58 /* GULReachabilityChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EFC3E9C2F0E1ABA696F73E63B829CE6 /* GULReachabilityChecker.m */; }; - 90BA3209CD339006E7B46A3410F4CD47 /* GULLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = A3E64E6865163D74A4A9C1720F021176 /* GULLogger.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 90FF5AE27672B3705B540430627AAE1A /* FIRApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 0721C680CFD7CCC281FAAE40CC7CC8FB /* FIRApp.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 93942FEE248D34A1409BA4C09935228D /* FIRInstanceIDStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 18437710751F31983A4B4343B6AE0846 /* FIRInstanceIDStringEncoding.m */; }; - 98C8F3CD30D7FC179619C4297EDFA954 /* FIRComponentContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B55F7049963BD05146CEAF24E766A28 /* FIRComponentContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 98E8C4DA7467C8A63B31CA042446E2E6 /* pb_encode.h in Headers */ = {isa = PBXBuildFile; fileRef = B61D10491AFFFCEF1A08FB83E4F44A0A /* pb_encode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9A1C90B1E05093DDF8B89EF59A6727AE /* GULSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CED92C605AFBFFF75E32B97F855D08 /* GULSwizzler.m */; }; - 9B06D94D480832601A64BE7F6D29532C /* FIRInstanceIDCheckinPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = EB68E9D6DF3F7CBD8B13F677C173E8E6 /* FIRInstanceIDCheckinPreferences.m */; }; - 9CB382D049F3DBBFEB176BEF0543DA34 /* FirebaseCore-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B1A17AD079D9B1CC5F2566896555AE /* FirebaseCore-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9E31E2EBECBBEE07EFC16DF386D6AE4E /* GULNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3D88362D7798228C9653A26168D35E /* GULNSData+zlib.m */; }; - A0779DD555A5A31F2302853261F22548 /* FIRInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = F5B51CAE6AAA7A3DEBCB59D9B03C90BE /* FIRInstanceID.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A14D03FFE5AB2DDE88377934063BF951 /* FirebaseCore-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A14A4950DB0EDF3B8A678040F6D096DB /* FirebaseCore-dummy.m */; }; - A4077CB32B233FA9A612685884A231A5 /* GULAppDelegateSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 65446BC662392EE8067E81346AB6D2E0 /* GULAppDelegateSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A64F4FE4D9093E1AE9EFEE5094DC0E43 /* FIRInstanceIDCheckinStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 94F3B242B6FA54A454F2784A59455D37 /* FIRInstanceIDCheckinStore.m */; }; - A764BBEB0892656245FE139E4F6EA311 /* FIRInstanceIDURLQueryItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CBD9DE48945BE5D8D3F6DF83BCAEBB7 /* FIRInstanceIDURLQueryItem.m */; }; - A8EB0C6CA4D63005360D86AA889A518A /* GULReachabilityChecker+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC7055B8AB094F4AC3432B809088C212 /* GULReachabilityChecker+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A8F30A21B9C02C553EDA86290D8C896F /* GULAppDelegateSwizzler_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 322F1229F2E3E83A51DF4376E1C3521A /* GULAppDelegateSwizzler_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - AA969B6FE2ED5EE3ACE1C2AC005701C0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - ABBA4D1A08A1D18ECF18CFCEC1D683DE /* FIRApp.m in Sources */ = {isa = PBXBuildFile; fileRef = B3DB5DA6985094E9B258EF0B816C7BAC /* FIRApp.m */; }; - AE273C13E5C6707E77A6B4EBFA096DA1 /* FIRInstanceIDUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC45FD590053D7BF61B9B75DB594D11 /* FIRInstanceIDUtilities.m */; }; - B078F7C21362CC2EC03671646B46739F /* FIRDependency.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BDD351D126C991113400163846774B8 /* FIRDependency.m */; }; - B2846F8C8EEB75BF6E4BCE173933460B /* FIRInstanceIDTokenDeleteOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 90D2864C001A2BBBDC9E2870EE0A692A /* FIRInstanceIDTokenDeleteOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B38F70804F3D61BBBA93329CA7ED0FC8 /* pb_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 8D8E654970A93A22D15B4DB3F736820B /* pb_decode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; }; - B52B5E4267D0030974343307EFCDE2F2 /* FIRInstanceIDCheckinPreferences_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 74804560F99741B16093413E3ED271A1 /* FIRInstanceIDCheckinPreferences_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B60CEC5F4A4DE230EA3EA08A13DDE360 /* FIRInstanceIDCheckinService.m in Sources */ = {isa = PBXBuildFile; fileRef = AD786D532408C86D1EB6C927B0C87A75 /* FIRInstanceIDCheckinService.m */; }; - B874D22C452407668077BA2AB7F14F2A /* FirebaseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1269954F868B70BE772E5B5D20024F /* FirebaseCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BB7CA1C508167B9F8FD455BEA2D522E3 /* GoogleUtilities-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A8182678D4ABD0F98642AD7406D1BCB2 /* GoogleUtilities-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C071C67F80CC4B0C3B8CF21F3F91CED0 /* FIRInstanceIDTokenInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = A3FE1AA32CB9692E06D6A3C4001459B8 /* FIRInstanceIDTokenInfo.m */; }; - C1C8CE593A5F1669B167AB171FB02783 /* FIRInstanceIDAPNSInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CC93151666D93519C2AC104874AD71FC /* FIRInstanceIDAPNSInfo.m */; }; - C40EB7CFC8729D308A3FC952DBEFDAC4 /* FIRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 7847D97A17737D2D6CB590B13CE564CC /* FIRConfiguration.m */; }; - C67A8DA52D8476D2E472A65E8ED2104C /* FIRInstanceIDUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 21652FC291F729CE79FCAA2D04B79CE8 /* FIRInstanceIDUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; - C89BB6836DEA8F2EE0B9199A7A78CDDC /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 099602E95CC02BDF479BAE577F2E1E33 /* SystemConfiguration.framework */; }; - CAA80C28BBEFFF683932412EFE2D52C8 /* FIRInstanceIDAuthService.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AF1A6BD7C03AE56C8644D4BF79E2E78 /* FIRInstanceIDAuthService.h */; settings = {ATTRIBUTES = (Project, ); }; }; - CD615CD72DECC08D3F0223C7E23B3093 /* FIRComponentContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = FCD467F2BC7ACAB860E989845586EE9A /* FIRComponentContainer.m */; }; - CDB71F03D6470F12E7217D87B7AC7AF0 /* FIRInstanceIDURLQueryItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 10DE2EBFE2C271E7C4FDB62DEB56A09E /* FIRInstanceIDURLQueryItem.h */; settings = {ATTRIBUTES = (Project, ); }; }; - D0684944EF39981421BA02602FB5B6BB /* FIRInstanceIDTokenDeleteOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 58F0868863F06C7865D23136C4091147 /* FIRInstanceIDTokenDeleteOperation.m */; }; - D09DE4F8CF9819C7BB99A345BBA78339 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A62F5DEA813F07D663C4B01BE3FDEA0 /* Security.framework */; }; - D0D85A8ED3641D03ECCFFD79CC11D9FF /* FIRInstanceIDStringEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = E66B7745DFD45E0844D344CE0A1B0425 /* FIRInstanceIDStringEncoding.h */; settings = {ATTRIBUTES = (Project, ); }; }; - D0FE30F9B8D24715714954E61B6B5F36 /* GULLoggerCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 807CA56F6A01FAE1DD7905DCD950E8CB /* GULLoggerCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2B0DFDD0967BE00F5CB9E1941A6D6C4 /* FIRInstanceID.m in Sources */ = {isa = PBXBuildFile; fileRef = 093DFF10EA7BD9115772D1D2AA477795 /* FIRInstanceID.m */; }; - D316314973075DB32C8953B79BF3BC75 /* FIRInstanceID+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = DEA3051E00CD0FC852F9913652FB7F93 /* FIRInstanceID+Private.m */; }; - D47EEC76008C543C3E272E8D810E1F3D /* Pods-TeachersAssistant-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DFABC77294AE1B3C79891E913990217 /* Pods-TeachersAssistant-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D61867C7D901892724105F7FAC29841E /* GULLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BE349DEB3CC69BD666C0E30A25A632E /* GULLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D780901FD66E3D4D29C65BD48F52340A /* GULNetworkMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5471452A72F4611E3232627BC06ADD9A /* GULNetworkMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; - D8A219829C97AE6AF93A048E09F5680D /* FIRComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 053056F16263D5265F32C049C2F8E01C /* FIRComponent.m */; }; - DE4E29330B8FF66A9D6931E4A35D011C /* FIRInstanceIDBackupExcludedPlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 36376442373649570509651445F690F2 /* FIRInstanceIDBackupExcludedPlist.m */; }; - E36F4E96A7B06203207C264C5ABDFD2E /* FIRInstanceIDAuthService.m in Sources */ = {isa = PBXBuildFile; fileRef = EF86BBD38F49FEFDB78AB8A8F0A5823A /* FIRInstanceIDAuthService.m */; }; - E6E306527999E035678E03FCFB44F5E6 /* FIROptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A1828B5340482EC8D3397627ACC5AB34 /* FIROptions.m */; }; - E6F14D70F8F878C0D7D8894FBA1D919D /* FIRInstanceIDVersionUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 76F8720EE48E3A1D2085F0BC9F06CEE5 /* FIRInstanceIDVersionUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E8C0A0E2222FF021FE0174A246F9AD21 /* FIRAppAssociationRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = ABE610632F28D152955ACE0CE2F28F03 /* FIRAppAssociationRegistration.m */; }; - EAAC31E35767E4527322215E77F500CB /* GULAppDelegateSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFE4C8CE0104CF4C505FE3DF5422F51 /* GULAppDelegateSwizzler.m */; }; - EBB9A6443A2A88303F5D58BC929E7832 /* FIRInstanceID+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 55866B95A313509A4206813BC4B48CD6 /* FIRInstanceID+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - ED4EAD8B9899E20BCA8955C010D69F81 /* FIRInstanceIDLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 87F87803FFB82959EC1D3D60F01B0458 /* FIRInstanceIDLogger.m */; }; - F0DD6165CFF72F6AC7EB24BB0F81F59A /* FIRBundleUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 639A8B82A185BC312DC3B6CAF5A62F01 /* FIRBundleUtil.h */; settings = {ATTRIBUTES = (Private, ); }; }; - F0F798FAF8F64C21D8A06294F9CCF82B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */; }; - F9F2828D09A41F9367A8E4D678439DFD /* FIRComponentType.m in Sources */ = {isa = PBXBuildFile; fileRef = ABA62F0D7466C8A6A84592F640B7D0E1 /* FIRComponentType.m */; }; - FA4879EE891E4376D1CB122873A0D774 /* FIROptionsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 66D59167D31E9AB0B577C1597DE53BDF /* FIROptionsInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; - FAFCFEADC58CB5BC49874CFEB7AB6508 /* FIRAnalyticsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEC7230B0E3D9061BB1D1B1D8369B51 /* FIRAnalyticsConfiguration.m */; }; - FB0C39A32EFA1192032C872DF986F94B /* FIRInstanceIDTokenManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 29E9D0A0280D12D4D31B3C8C33CB540A /* FIRInstanceIDTokenManager.m */; }; - FBB36F8F7338268B4BCFD63D6CDDBE99 /* GULNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = DF3447D3504A8DEEBC66BBF47DA3AED9 /* GULNSData+zlib.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FEA8B7A05B7B352EE64E0FD4C79D720D /* FIRInstanceIDTokenFetchOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E439097F5D3E57E08E7B5A0CF53E344 /* FIRInstanceIDTokenFetchOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6C8C3F762C321C7FCF5D7A91E0CBD4B1 /* GULNetworkLoggerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 00061B4668B2781E6A386D5E815509C2 /* GULNetworkLoggerProtocol.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 6D61C466E6E69A842B65285DE2261213 /* GTLRService.m in Sources */ = {isa = PBXBuildFile; fileRef = B9EDA8960D68B07F4E048AFDDA68BCA0 /* GTLRService.m */; }; + 6DEE0C82F00E41474F70E54D39C1337D /* GTLRSheetsQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 20CB19971EEB7403662B8D6D969AAA1B /* GTLRSheetsQuery.m */; }; + 6EC07394A547C7982468ED9069AAB53B /* FIRInstanceIDVersionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = E83DAD359A051E38E0DCAAC7824D73BE /* FIRInstanceIDVersionUtilities.m */; }; + 6FD0FBAB532F0C93B7B8909FC99BFE97 /* FIRInstanceIDBackupExcludedPlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 108C5CDFDC3BEA327489B1E055ED7470 /* FIRInstanceIDBackupExcludedPlist.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 723B1DEC2C8098017A02DF898B87E8D0 /* GULLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 21E1BF177C744299F7CB68726168AE6D /* GULLogger.m */; }; + 730879B4705522202D09520FBDEE11A5 /* GTLRDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 56CE98F133AB2BA1382EB2FAB97B0AF8 /* GTLRDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 76478F8839B3E900D460C0DE15D21F53 /* FIRErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0DA168C2F41D42C624551BC2C23F9D /* FIRErrors.m */; }; + 764B2AE2F2B17F964F6437AE468C0F15 /* FIRInstanceIDTokenOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 571008D58CCCDD41BFC7992FC649BC4D /* FIRInstanceIDTokenOperation.m */; }; + 7B4645279AD67C809174C1B58AD6F915 /* FIRInstanceIDTokenStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DF78EC6817986F64D37F229896D32E8 /* FIRInstanceIDTokenStore.m */; }; + 7D1EE73692570398E3148C707E474265 /* GTLRErrorObject.h in Headers */ = {isa = PBXBuildFile; fileRef = F6D4CCD7AF5F1B7C6D6D88509BAB7785 /* GTLRErrorObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7D49364D45BC35C596753015EED2158D /* FIRInstanceIDTokenManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F7812E7B55B6200DF820251D9207F56 /* FIRInstanceIDTokenManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7E169F6567DADEB4C0269E64A51E6DA4 /* GULReachabilityMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DA698D652FCFAC16C9EEFCA83C85E72 /* GULReachabilityMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 7F6426897180E49EE98F3A6CE8F24D58 /* FIRInstanceIDLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 43D7AEB5BE9D344529D075789E88E926 /* FIRInstanceIDLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7FB4AACD60131CB28B62F5EDB531AAA4 /* FIRInstanceIDCheckinPreferences+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = A6EFE17E8C1D2435651AA1CF3C3314CB /* FIRInstanceIDCheckinPreferences+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7FE7DA15DFBB03C6F320F4137F0284ED /* GULSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B5032812C848FA7747EE94B157813B2 /* GULSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8343914CF540F43454F0A80F319961E8 /* FIRAnalyticsConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = ED74DCD72556A6C8618B511D2910AB18 /* FIRAnalyticsConfiguration+Internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 854834B0636DAEB384EAC5BBFA00993A /* FIRComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 499E59FADC4F9A4FD57637B40A7E0AEB /* FIRComponent.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 876F2110FD88F97EDECF6652D7193836 /* FIRIMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4094FA6772FCD62F992E63FD03A145DC /* FIRIMessageCode.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 88B58642B5770D7313961DB5AAC02C58 /* GULReachabilityChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 5422968193C250477308AB011920863A /* GULReachabilityChecker.m */; }; + 8E0A60B68DDAD2843111B1C139A1C29E /* GTLRDateTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 0810102125B8C9E4FBBF7EF7D58409CC /* GTLRDateTime.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8F07CBB8F46A54B2AE38082096AAC033 /* GTLRQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 91FD3DD45495B59CF1AD18C2F1C5664A /* GTLRQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9043BA2AD19DEE4622D0572578355D84 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + 90B3B87D94A9F9F6A90C2222E7516DC9 /* GTMSessionUploadFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E8A41DFF31FE019B30BF0B9770BE14D /* GTMSessionUploadFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90BA3209CD339006E7B46A3410F4CD47 /* GULLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = CD7867B1787870FFF4445F7C463304EC /* GULLogger.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 90FF5AE27672B3705B540430627AAE1A /* FIRApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAFBE6129D7F7EE3751814049C55310 /* FIRApp.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 93942FEE248D34A1409BA4C09935228D /* FIRInstanceIDStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = D3389939168D7E16A07E06299C2D2CC6 /* FIRInstanceIDStringEncoding.m */; }; + 93F238A3CBF92469EAB23AA952814B9E /* GoogleAPIClientForREST-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 359D1387EF23CF339959AB5D298D182D /* GoogleAPIClientForREST-dummy.m */; }; + 9597B01BCD32158B7674DA270A77A05D /* GTLRBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = A12B17E00FB8FCB54E94D10975B5302C /* GTLRBase64.m */; }; + 98C8F3CD30D7FC179619C4297EDFA954 /* FIRComponentContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AF638AC9C581DA7EAF1350F43C6E347 /* FIRComponentContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 98E8C4DA7467C8A63B31CA042446E2E6 /* pb_encode.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F783D0C9291771FE7CD74A47090BDE4 /* pb_encode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A1C90B1E05093DDF8B89EF59A6727AE /* GULSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = E141DD28AFD58761041D850CDAE0C508 /* GULSwizzler.m */; }; + 9B06D94D480832601A64BE7F6D29532C /* FIRInstanceIDCheckinPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = B2CE9C8D3C9509E03742DAAC7D484973 /* FIRInstanceIDCheckinPreferences.m */; }; + 9BBC4EFFC569655FBA0A52C627D674F0 /* GTLRQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F922D095E5B9A857BABDE9A29D2EA7 /* GTLRQuery.m */; }; + 9CB382D049F3DBBFEB176BEF0543DA34 /* FirebaseCore-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7110D63F53E9A413E26235ED20E973C9 /* FirebaseCore-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9E31E2EBECBBEE07EFC16DF386D6AE4E /* GULNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 09B65EA9CD18054C3731D78BF06AFBBC /* GULNSData+zlib.m */; }; + A0779DD555A5A31F2302853261F22548 /* FIRInstanceID.h in Headers */ = {isa = PBXBuildFile; fileRef = 30630E98C2C2879DE2C1248CA2C52F19 /* FIRInstanceID.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A14D03FFE5AB2DDE88377934063BF951 /* FirebaseCore-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 312A54AECBD7450DEF1199ACE70B112A /* FirebaseCore-dummy.m */; }; + A3A05DE0E3AEB3299F7606F0F8BD7756 /* GTMSessionFetcherLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A9E981E17F06280BA9E8358D8FB90FD /* GTMSessionFetcherLogging.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A4077CB32B233FA9A612685884A231A5 /* GULAppDelegateSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = F171712428FC85037E9F00703B7F2E22 /* GULAppDelegateSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A64F4FE4D9093E1AE9EFEE5094DC0E43 /* FIRInstanceIDCheckinStore.m in Sources */ = {isa = PBXBuildFile; fileRef = DF5BFCC018CAF9606683F89394C3B25B /* FIRInstanceIDCheckinStore.m */; }; + A764BBEB0892656245FE139E4F6EA311 /* FIRInstanceIDURLQueryItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 03CAC1CE04C75824406C3CB91E3A6CB2 /* FIRInstanceIDURLQueryItem.m */; }; + A8EB0C6CA4D63005360D86AA889A518A /* GULReachabilityChecker+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E00DA041CCC31B6AEC875C52444A935 /* GULReachabilityChecker+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A8F30A21B9C02C553EDA86290D8C896F /* GULAppDelegateSwizzler_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = A3EAF3AA8E5BADD73BCE0D2B8CC69B98 /* GULAppDelegateSwizzler_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA969B6FE2ED5EE3ACE1C2AC005701C0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + ABA437DFA30B41D0B77E0592682739FE /* GTLRBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 632CEDCB8B1B6CADF8FA17EE175367B8 /* GTLRBase64.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ABBA4D1A08A1D18ECF18CFCEC1D683DE /* FIRApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 271F6CEBA30B3B7191840DEDD47F81FC /* FIRApp.m */; }; + AE273C13E5C6707E77A6B4EBFA096DA1 /* FIRInstanceIDUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DFE9A713C35D87D3276D66BDED99B818 /* FIRInstanceIDUtilities.m */; }; + B078F7C21362CC2EC03671646B46739F /* FIRDependency.m in Sources */ = {isa = PBXBuildFile; fileRef = 0508F9F459470F87AE900EE671A33683 /* FIRDependency.m */; }; + B1A5C2FE1615E297E4A245C34B7F6762 /* GTMSessionFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 504D2264E8585EE42A61DCE121FA7334 /* GTMSessionFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B2846F8C8EEB75BF6E4BCE173933460B /* FIRInstanceIDTokenDeleteOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = D9BB6D0D6107EB6AA9292CBA994F58BD /* FIRInstanceIDTokenDeleteOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B38F70804F3D61BBBA93329CA7ED0FC8 /* pb_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = CC33E734D1611BA6A191D8246CEFF204 /* pb_decode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; }; + B52B5E4267D0030974343307EFCDE2F2 /* FIRInstanceIDCheckinPreferences_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D9107B74D49E38BCF7B3F2ED77E71F21 /* FIRInstanceIDCheckinPreferences_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B60CEC5F4A4DE230EA3EA08A13DDE360 /* FIRInstanceIDCheckinService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AA2DD0AD8A2506D0FC3BBBF05067226 /* FIRInstanceIDCheckinService.m */; }; + B874D22C452407668077BA2AB7F14F2A /* FirebaseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AA8AEFC53E239B1C44121C6399BDF14 /* FirebaseCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BAEA0165416D2478BB040F1584A157E9 /* GTLRBatchResult.m in Sources */ = {isa = PBXBuildFile; fileRef = ED28B4447BE06C582F74CF53046DEFA2 /* GTLRBatchResult.m */; }; + BB7CA1C508167B9F8FD455BEA2D522E3 /* GoogleUtilities-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 84B75F5A2DD728216A8F32ED58EB0EEC /* GoogleUtilities-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C071C67F80CC4B0C3B8CF21F3F91CED0 /* FIRInstanceIDTokenInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 93FB1D3F1A434DADF7E8F2E0DCDF6CB4 /* FIRInstanceIDTokenInfo.m */; }; + C191B7AFCB86D9C55E552A9432A08AA5 /* Pods-TeachersAssistant-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 74104373A68924B948E2FD66656DDF04 /* Pods-TeachersAssistant-dummy.m */; }; + C1C8CE593A5F1669B167AB171FB02783 /* FIRInstanceIDAPNSInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 580320C3A739577369ED81033BFCE123 /* FIRInstanceIDAPNSInfo.m */; }; + C40EB7CFC8729D308A3FC952DBEFDAC4 /* FIRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BB2879569D536A6530B687161DC8B1 /* FIRConfiguration.m */; }; + C67A8DA52D8476D2E472A65E8ED2104C /* FIRInstanceIDUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C252AD090AF9844AB7721AC0D603C74 /* FIRInstanceIDUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C89BB6836DEA8F2EE0B9199A7A78CDDC /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F0A5A680330D1BBE6109D7E21EEBA20 /* SystemConfiguration.framework */; }; + CAA80C28BBEFFF683932412EFE2D52C8 /* FIRInstanceIDAuthService.h in Headers */ = {isa = PBXBuildFile; fileRef = 1911D2DEEADE2AAC254508D0D8D4DD05 /* FIRInstanceIDAuthService.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CD615CD72DECC08D3F0223C7E23B3093 /* FIRComponentContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E3ADB623A71EF1040F07F4474B13C1F /* FIRComponentContainer.m */; }; + CDB71F03D6470F12E7217D87B7AC7AF0 /* FIRInstanceIDURLQueryItem.h in Headers */ = {isa = PBXBuildFile; fileRef = B5F6A2DBC7DB5FAD70AD67EE2F401420 /* FIRInstanceIDURLQueryItem.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CE0AF5ABE638F0F58F8DC3D81B7E3100 /* GTMReadMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 547B4BA4FC4AC94B80BCCBAA38BEEB88 /* GTMReadMonitorInputStream.m */; }; + CF12D67880876BC3C2360BB7F3C792ED /* GTLRUploadParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 6004EF0F526D87CB1DB9CE9FE43FBE40 /* GTLRUploadParameters.m */; }; + D044612547CC780C57BA3DDF204F4550 /* GTMGatherInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC79991521336223B763662128BB7A3 /* GTMGatherInputStream.m */; }; + D0684944EF39981421BA02602FB5B6BB /* FIRInstanceIDTokenDeleteOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F74B35ABB197CD426E4B528256BA88 /* FIRInstanceIDTokenDeleteOperation.m */; }; + D09DE4F8CF9819C7BB99A345BBA78339 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D669732E0BA0105016B0BFAF2C9EEE1 /* Security.framework */; }; + D0D85A8ED3641D03ECCFFD79CC11D9FF /* FIRInstanceIDStringEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AA55CFA88ACE2B99420F368974ED9E7 /* FIRInstanceIDStringEncoding.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D0FE30F9B8D24715714954E61B6B5F36 /* GULLoggerCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 33E015D9CF73817DBAE5EE02250E5DED /* GULLoggerCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D2B0DFDD0967BE00F5CB9E1941A6D6C4 /* FIRInstanceID.m in Sources */ = {isa = PBXBuildFile; fileRef = 49C0CD9206E359DF82CB5D71E5B5097F /* FIRInstanceID.m */; }; + D316314973075DB32C8953B79BF3BC75 /* FIRInstanceID+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = E62986E64C2AD9319C9BE39EFAD551E2 /* FIRInstanceID+Private.m */; }; + D61867C7D901892724105F7FAC29841E /* GULLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 58AC66E22BF3126CF2AB5A768FAABD0F /* GULLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D775E01ED0653D1AA554B9D50E9C276E /* GTLRRuntimeCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = C9B714FB115F77DF3A9116B5D1D89C31 /* GTLRRuntimeCommon.m */; }; + D780901FD66E3D4D29C65BD48F52340A /* GULNetworkMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F130B340A708E32CAEA83F0B6619B08 /* GULNetworkMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D79F7227DECCB6A36A093DC50BDEE07E /* GTLRObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D9A4276C20DF4C737ECB8CD0541D1A67 /* GTLRObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8A219829C97AE6AF93A048E09F5680D /* FIRComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AC4E56443368A59F770E05990328FBB /* FIRComponent.m */; }; + D90D03372B914B0F17346CFA70DE4BEC /* GTLRURITemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = DCEE515F01DF166DBC634C843933F6B9 /* GTLRURITemplate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D90D2E30A9BE48C385E0B6F2A40A8924 /* GTLRRuntimeCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AF84FD306398BAAB32D5EB452C1E569 /* GTLRRuntimeCommon.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D9CAB0323F055B25038E725BDF34909A /* GTLRBatchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = ADB38B25ABA9170879CD55B53412DFE6 /* GTLRBatchResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE33BF21B7C31BD920D551FAAB7EA056 /* GoogleAPIClientForREST-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AB3ECC077B85578CA86B1AB55A7B12D /* GoogleAPIClientForREST-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE4E29330B8FF66A9D6931E4A35D011C /* FIRInstanceIDBackupExcludedPlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 516450B002D7F6CFAD2116CE501F57A3 /* FIRInstanceIDBackupExcludedPlist.m */; }; + DE84A495B46D938B298901309B53C60A /* GTMSessionFetcher-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F000C09E14723D2EBBD391E19D9D5FBF /* GTMSessionFetcher-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DF87CF5ECCAE4379FB2B17ACD054F1F2 /* GTLRURITemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6FFA90600425E042D0DB9FE2BCF44F /* GTLRURITemplate.m */; }; + E16C9C03AFF3F2637540647FB777E3A0 /* GTLRService.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F3C1179BBE0B2F619AB8D842242DF54 /* GTLRService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E32F693371E7272421A8F5ABE4C7D05B /* GTLRBatchQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 73DEB6CDF7421F78C01F7474A67404F3 /* GTLRBatchQuery.m */; }; + E36F4E96A7B06203207C264C5ABDFD2E /* FIRInstanceIDAuthService.m in Sources */ = {isa = PBXBuildFile; fileRef = A36649E2942CA43CF746CB8BB3356A9D /* FIRInstanceIDAuthService.m */; }; + E6E306527999E035678E03FCFB44F5E6 /* FIROptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA86BE4F12D473D435458BB4FB158BCD /* FIROptions.m */; }; + E6F14D70F8F878C0D7D8894FBA1D919D /* FIRInstanceIDVersionUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3693350ACB41663D2E80F539D7E5C50E /* FIRInstanceIDVersionUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E8C0A0E2222FF021FE0174A246F9AD21 /* FIRAppAssociationRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = B6FBF86619741BAE53E3D255FBF0ABB1 /* FIRAppAssociationRegistration.m */; }; + E9D3144ECE2DB2BCCA436A874DE7EC89 /* GTLRSheetsObjects.m in Sources */ = {isa = PBXBuildFile; fileRef = B17C169B2ADA959DE38DAE86D07B7379 /* GTLRSheetsObjects.m */; }; + EA0876B4F2B40E37E01E0D9F96BA34B3 /* GTMReadMonitorInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 8795560D80511CF84F3EF6325D2A8F51 /* GTMReadMonitorInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EAAC31E35767E4527322215E77F500CB /* GULAppDelegateSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 64A59B0655E40448F960D448BFE2B4D4 /* GULAppDelegateSwizzler.m */; }; + EBB9A6443A2A88303F5D58BC929E7832 /* FIRInstanceID+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C6C4BA62B48087FCEC1011C608BDBA /* FIRInstanceID+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + ED4EAD8B9899E20BCA8955C010D69F81 /* FIRInstanceIDLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = A737C343B6C79B154315978701F29A04 /* FIRInstanceIDLogger.m */; }; + F03936351B8D3C6B5E7D5E58703FF5BE /* GTMSessionFetcher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B0CAE77A1601E438C553BCD88A71A50 /* GTMSessionFetcher.framework */; }; + F0DD6165CFF72F6AC7EB24BB0F81F59A /* FIRBundleUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = E150DF4221857596EE237D4604C64D39 /* FIRBundleUtil.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F0F798FAF8F64C21D8A06294F9CCF82B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */; }; + F1D4FF2068E4D0B9395A2B253CF7098C /* GTMSessionFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CB4C2370A78826FB61E74033B2F9BFF /* GTMSessionFetcher.m */; }; + F4E0A3D1D35780BFF837007FA581A4F0 /* GTLRSheetsQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = C635ED3519BFC49EB2C63A4252200FF2 /* GTLRSheetsQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F9F2828D09A41F9367A8E4D678439DFD /* FIRComponentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CC67CCBC2D46F79CC57C3F798122C7E /* FIRComponentType.m */; }; + FA4879EE891E4376D1CB122873A0D774 /* FIROptionsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A3A538DA0C18DD8A698010361D27A8A /* FIROptionsInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + FAFCFEADC58CB5BC49874CFEB7AB6508 /* FIRAnalyticsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D74E330C10A59ECB0E3A651183B5902 /* FIRAnalyticsConfiguration.m */; }; + FB0C39A32EFA1192032C872DF986F94B /* FIRInstanceIDTokenManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F38AE2C8EE17A7657996595BFC9ABE11 /* FIRInstanceIDTokenManager.m */; }; + FBB36F8F7338268B4BCFD63D6CDDBE99 /* GULNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = F0D1F45EB0BE7940F2B0A3B152F0BAF0 /* GULNSData+zlib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FEA8B7A05B7B352EE64E0FD4C79D720D /* FIRInstanceIDTokenFetchOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = CFF309BE82E37F7CBFD6B6D48621D286 /* FIRInstanceIDTokenFetchOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -209,12 +267,26 @@ remoteGlobalIDString = 01B53B6A43CBD6D4022A361BBFCCE665; remoteInfo = FirebaseCore; }; - 402E60D1719D9B9FF37FA3105F6DCF2D /* PBXContainerItemProxy */ = { + 0F60F822741F17E09A70AC758014F400 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = D9A2B7F6350AE8AB9AAFF5A9395AD63C; - remoteInfo = GoogleUtilities; + remoteGlobalIDString = 799B29F9D6DCE28B98CC259440382F20; + remoteInfo = Firebase; + }; + 1978C06F97680C1C8CFE2AC9F848F053 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 01B53B6A43CBD6D4022A361BBFCCE665; + remoteInfo = FirebaseCore; + }; + 449060759393A041D614674D0E5B2085 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 232D00D8ED7797390FB38004DE01723B; + remoteInfo = FirebaseAnalytics; }; 455009ED9ED8F59E3D7880EA52A66B11 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -230,6 +302,13 @@ remoteGlobalIDString = D9A2B7F6350AE8AB9AAFF5A9395AD63C; remoteInfo = GoogleUtilities; }; + 475E73100BA3CA8F024336F74263C678 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57B9E0A892EAB5C13D4AE7D4B1DE0C16; + remoteInfo = GoogleAppMeasurement; + }; 53E2A1BD19729C2293AB46582C686251 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; @@ -244,12 +323,12 @@ remoteGlobalIDString = D9A2B7F6350AE8AB9AAFF5A9395AD63C; remoteInfo = GoogleUtilities; }; - 5863AE56A2562B5D081E6742D63982F4 /* PBXContainerItemProxy */ = { + 54C6A825F7C3057AC5EB47B5EDA879F3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 232D00D8ED7797390FB38004DE01723B; - remoteInfo = FirebaseAnalytics; + remoteGlobalIDString = D9A2B7F6350AE8AB9AAFF5A9395AD63C; + remoteInfo = GoogleUtilities; }; 5BE488B88EB1D7B8BFE4A63D278D4B18 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -258,12 +337,12 @@ remoteGlobalIDString = D9A2B7F6350AE8AB9AAFF5A9395AD63C; remoteInfo = GoogleUtilities; }; - 6E678B000225AD45F3BA9059ED4E8485 /* PBXContainerItemProxy */ = { + 63982F044D1394A02009890DC4D11191 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 3C6A9BF574C3488966C92C6A9B93CA8C; - remoteInfo = FirebaseInstanceID; + remoteGlobalIDString = E93C48A48FB03EA19C4F756B97B5F1D3; + remoteInfo = nanopb; }; 7AEA5761B26CAEF1A0C0E82599059DA8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -272,19 +351,26 @@ remoteGlobalIDString = 232D00D8ED7797390FB38004DE01723B; remoteInfo = FirebaseAnalytics; }; - AA9AF4CB5DB51B15FD03B4B06A1E364F /* PBXContainerItemProxy */ = { + 94497EC05AB24679A89D934FDF900750 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 01B53B6A43CBD6D4022A361BBFCCE665; - remoteInfo = FirebaseCore; + remoteGlobalIDString = 3C6A9BF574C3488966C92C6A9B93CA8C; + remoteInfo = FirebaseInstanceID; }; - AD3680B60351F3B4F1A057686D61E059 /* PBXContainerItemProxy */ = { + 98553A26A4F1509909FEE91EA0162432 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 799B29F9D6DCE28B98CC259440382F20; - remoteInfo = Firebase; + remoteGlobalIDString = D811FCD6F8EBFE221BF9D923CEFB5CE8; + remoteInfo = GTMSessionFetcher; + }; + B82C96A38C8E60FE2CEB597BB1AC15D7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 772340DB04E857CA1ABBDB7EC1CA7079; + remoteInfo = GoogleAPIClientForREST; }; BBDC7C661CA5567D3925BC0747CAAEC5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -311,22 +397,22 @@ isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = E6548F36829F0F6D877D829B97A54A90; + remoteGlobalIDString = C1DBAA447353B7E3A4A495D068C464D8; remoteInfo = "Pods-TeachersAssistant"; }; - DC109F4C3DFF347305FC87301148EE99 /* PBXContainerItemProxy */ = { + DF12C5D7BB68C2724D2F39A531F2A52A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = E93C48A48FB03EA19C4F756B97B5F1D3; remoteInfo = nanopb; }; - DF12C5D7BB68C2724D2F39A531F2A52A /* PBXContainerItemProxy */ = { + F57E55D97C9AF0B37D53F628359ABA5F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = E93C48A48FB03EA19C4F756B97B5F1D3; - remoteInfo = nanopb; + remoteGlobalIDString = D811FCD6F8EBFE221BF9D923CEFB5CE8; + remoteInfo = GTMSessionFetcher; }; F6A14184DE3C02C257A7298719E4FD9B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -335,205 +421,263 @@ remoteGlobalIDString = 01B53B6A43CBD6D4022A361BBFCCE665; remoteInfo = FirebaseCore; }; - FB82F0ED3E43996A5BB572C2CDA1A7C3 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 57B9E0A892EAB5C13D4AE7D4B1DE0C16; - remoteInfo = GoogleAppMeasurement; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 01168DD1A418F79F4BAAA97A8902A73A /* GULMutableDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULMutableDictionary.m; path = GoogleUtilities/Network/GULMutableDictionary.m; sourceTree = "<group>"; }; - 0197914611D5254348329F289DE67F28 /* FIRInstanceIDKeyPairStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPairStore.m; path = Firebase/InstanceID/FIRInstanceIDKeyPairStore.m; sourceTree = "<group>"; }; - 01B1A17AD079D9B1CC5F2566896555AE /* FirebaseCore-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCore-umbrella.h"; sourceTree = "<group>"; }; + 00061B4668B2781E6A386D5E815509C2 /* GULNetworkLoggerProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkLoggerProtocol.h; path = GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h; sourceTree = "<group>"; }; + 017F9C2BF8786F97CC75BB1728EC1B83 /* FirebaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCore.framework; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 029A66B7819D4FD2C4DD1328C5488419 /* Pods-TeachersAssistantTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TeachersAssistantTests-umbrella.h"; sourceTree = "<group>"; }; - 034660AA71C364B6CE19A7775ACBFF3F /* FIRInstanceIDKeyPairStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPairStore.h; path = Firebase/InstanceID/FIRInstanceIDKeyPairStore.h; sourceTree = "<group>"; }; - 053056F16263D5265F32C049C2F8E01C /* FIRComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponent.m; path = Firebase/Core/FIRComponent.m; sourceTree = "<group>"; }; - 06191F1C90B569E50C136B8829E2546A /* GULReachabilityMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityMessageCode.h; path = GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h; sourceTree = "<group>"; }; - 0721C680CFD7CCC281FAAE40CC7CC8FB /* FIRApp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRApp.h; path = Firebase/Core/Public/FIRApp.h; sourceTree = "<group>"; }; - 08B3E50C44804158B340813635E6BC09 /* FIRInstanceIDCheckinService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinService.h; path = Firebase/InstanceID/FIRInstanceIDCheckinService.h; sourceTree = "<group>"; }; - 093DFF10EA7BD9115772D1D2AA477795 /* FIRInstanceID.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceID.m; path = Firebase/InstanceID/FIRInstanceID.m; sourceTree = "<group>"; }; - 09461C56109A35828704974C4C9E9614 /* FIRComponent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponent.h; path = Firebase/Core/Private/FIRComponent.h; sourceTree = "<group>"; }; - 099602E95CC02BDF479BAE577F2E1E33 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; - 0BC59F9C9C92C79BD213359E59BC0083 /* GULNetwork.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetwork.h; path = GoogleUtilities/Network/Private/GULNetwork.h; sourceTree = "<group>"; }; - 0CE321B66335679D34B98DD90B325217 /* FIRAppAssociationRegistration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppAssociationRegistration.h; path = Firebase/Core/Private/FIRAppAssociationRegistration.h; sourceTree = "<group>"; }; - 0EB8F5A1B2B1B249D6F54B422FAEF791 /* Firebase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Firebase.h; path = CoreOnly/Sources/Firebase.h; sourceTree = "<group>"; }; - 10C2190ED503628386EB3FA0ACBBBC13 /* pb_common.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_common.h; sourceTree = "<group>"; }; - 10DE2EBFE2C271E7C4FDB62DEB56A09E /* FIRInstanceIDURLQueryItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDURLQueryItem.h; path = Firebase/InstanceID/FIRInstanceIDURLQueryItem.h; sourceTree = "<group>"; }; + 03CAC1CE04C75824406C3CB91E3A6CB2 /* FIRInstanceIDURLQueryItem.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDURLQueryItem.m; path = Firebase/InstanceID/FIRInstanceIDURLQueryItem.m; sourceTree = "<group>"; }; + 042BE6629EC9A2E9F87D17CED7AACB6A /* FIRAppInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppInternal.h; path = Firebase/Core/Private/FIRAppInternal.h; sourceTree = "<group>"; }; + 0508F9F459470F87AE900EE671A33683 /* FIRDependency.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRDependency.m; path = Firebase/Core/FIRDependency.m; sourceTree = "<group>"; }; + 069518B8B1116A7286048E10777CE4B1 /* Firebase.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Firebase.xcconfig; sourceTree = "<group>"; }; + 07C940E5390B103F4CE2892BFFBC987A /* FIRConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRConfiguration.h; path = Firebase/Core/Public/FIRConfiguration.h; sourceTree = "<group>"; }; + 0810102125B8C9E4FBBF7EF7D58409CC /* GTLRDateTime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRDateTime.h; path = Source/Objects/GTLRDateTime.h; sourceTree = "<group>"; }; + 09B65EA9CD18054C3731D78BF06AFBBC /* GULNSData+zlib.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "GULNSData+zlib.m"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.m"; sourceTree = "<group>"; }; + 09CF74BB4F08D9BA981B0FCB07128326 /* GTMSessionFetcher-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GTMSessionFetcher-dummy.m"; sourceTree = "<group>"; }; + 0A22E7D0390892ECB350FFF998A271D0 /* GTMSessionFetcher.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GTMSessionFetcher.xcconfig; sourceTree = "<group>"; }; + 0AA2DD0AD8A2506D0FC3BBBF05067226 /* FIRInstanceIDCheckinService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinService.m; path = Firebase/InstanceID/FIRInstanceIDCheckinService.m; sourceTree = "<group>"; }; + 0AEFDB9DD19C098BAA39381962D72398 /* GULUserDefaults.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULUserDefaults.m; path = GoogleUtilities/UserDefaults/GULUserDefaults.m; sourceTree = "<group>"; }; + 0B5032812C848FA7747EE94B157813B2 /* GULSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSwizzler.h; path = GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h; sourceTree = "<group>"; }; + 0BBF3A739B0E8360360E868AA65D007D /* FIRInstanceIDCheckinStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinStore.h; path = Firebase/InstanceID/FIRInstanceIDCheckinStore.h; sourceTree = "<group>"; }; + 0CA616F95FA0F7EB03F1582C09FCD657 /* GTMSessionFetcherLogging.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMSessionFetcherLogging.m; path = Source/GTMSessionFetcherLogging.m; sourceTree = "<group>"; }; + 0E9773C37D034C604399ABF8364696FC /* GTLRSheets.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRSheets.h; path = Source/GeneratedServices/Sheets/GTLRSheets.h; sourceTree = "<group>"; }; + 0F0A5A680330D1BBE6109D7E21EEBA20 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; + 108C5CDFDC3BEA327489B1E055ED7470 /* FIRInstanceIDBackupExcludedPlist.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDBackupExcludedPlist.h; path = Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h; sourceTree = "<group>"; }; + 115AA047B525A10FCEE3748D755468AF /* GoogleAPIClientForREST.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleAPIClientForREST.framework; path = GoogleAPIClientForREST.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 118B269F032567135C30C461CA7A23AD /* Pods-TeachersAssistant.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-TeachersAssistant.modulemap"; sourceTree = "<group>"; }; + 1201678D8F7875046192E6EF9294A1C4 /* GULMutableDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULMutableDictionary.m; path = GoogleUtilities/Network/GULMutableDictionary.m; sourceTree = "<group>"; }; 123565447F11349B461EAA44530EFD01 /* Pods-TeachersAssistantTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TeachersAssistantTests-acknowledgements.plist"; sourceTree = "<group>"; }; - 15E28D734540855F231814108CF62173 /* FirebaseAnalytics.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseAnalytics.xcconfig; sourceTree = "<group>"; }; - 16ABBA3AED50ADB109B70827858F46FB /* FIRInstanceIDAuthKeyChain.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAuthKeyChain.m; path = Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m; sourceTree = "<group>"; }; - 16D15E2B7F18A663F0CB385DB75F0DC1 /* FIRInstanceIDTokenOperation+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceIDTokenOperation+Private.h"; path = "Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h"; sourceTree = "<group>"; }; - 16F8B483F73D5BC309CCBD721A6B71C0 /* GULNetworkURLSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkURLSession.h; path = GoogleUtilities/Network/Private/GULNetworkURLSession.h; sourceTree = "<group>"; }; + 123604314885A641B32BDF8B0F9DA302 /* FirebaseAnalytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseAnalytics.framework; path = Frameworks/FirebaseAnalytics.framework; sourceTree = "<group>"; }; + 1331D13DD4DE386BCEDBC001F6B10B28 /* FIRInstanceIDAPNSInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAPNSInfo.h; path = Firebase/InstanceID/FIRInstanceIDAPNSInfo.h; sourceTree = "<group>"; }; + 15D8C40F17E82EDAAA2FD2FEE97387AD /* FirebaseInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseInstanceID.h; path = Firebase/InstanceID/Public/FirebaseInstanceID.h; sourceTree = "<group>"; }; 176E21E234D1EF72C0869C40836ECBCC /* Pods-TeachersAssistantTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TeachersAssistantTests-dummy.m"; sourceTree = "<group>"; }; - 182BE2FC262ED73F2E01FFAF232D570E /* FIRDependency.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRDependency.h; path = Firebase/Core/Private/FIRDependency.h; sourceTree = "<group>"; }; - 18437710751F31983A4B4343B6AE0846 /* FIRInstanceIDStringEncoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDStringEncoding.m; path = Firebase/InstanceID/FIRInstanceIDStringEncoding.m; sourceTree = "<group>"; }; - 21652FC291F729CE79FCAA2D04B79CE8 /* FIRInstanceIDUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDUtilities.h; path = Firebase/InstanceID/FIRInstanceIDUtilities.h; sourceTree = "<group>"; }; - 24CF8D872F7CF1995FC4C686A806A211 /* FirebaseCore-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "FirebaseCore-Info.plist"; sourceTree = "<group>"; }; - 29E9D0A0280D12D4D31B3C8C33CB540A /* FIRInstanceIDTokenManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenManager.m; path = Firebase/InstanceID/FIRInstanceIDTokenManager.m; sourceTree = "<group>"; }; - 2A62F5DEA813F07D663C4B01BE3FDEA0 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; - 2A6F1873C3979691F097FF8841EBBD85 /* FIRErrors.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRErrors.m; path = Firebase/Core/FIRErrors.m; sourceTree = "<group>"; }; - 2A7C56749F7DFBFB68E361532C23A68A /* FIRVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRVersion.m; path = Firebase/Core/FIRVersion.m; sourceTree = "<group>"; }; - 2A95F0619B59607B38D60DFC6A5815C4 /* GULLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULLogger.m; path = GoogleUtilities/Logger/GULLogger.m; sourceTree = "<group>"; }; - 2AF1A6BD7C03AE56C8644D4BF79E2E78 /* FIRInstanceIDAuthService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAuthService.h; path = Firebase/InstanceID/FIRInstanceIDAuthService.h; sourceTree = "<group>"; }; - 2B6E9D37CB97B82EC30EDA29C24BFA28 /* FIRInstanceIDTokenStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenStore.m; path = Firebase/InstanceID/FIRInstanceIDTokenStore.m; sourceTree = "<group>"; }; - 2C6DFF7CA7C4CD341941DDF6A70B8A04 /* FirebaseInstanceID-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "FirebaseInstanceID-Info.plist"; sourceTree = "<group>"; }; - 2F822B32EED40D27314DFE21C5EDDA6A /* GULAppEnvironmentUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppEnvironmentUtil.m; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m; sourceTree = "<group>"; }; - 30CA044A7E9853B9620403801FFD468A /* FIRInstanceIDDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDDefines.h; path = Firebase/InstanceID/FIRInstanceIDDefines.h; sourceTree = "<group>"; }; - 322F1229F2E3E83A51DF4376E1C3521A /* GULAppDelegateSwizzler_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler_Private.h; path = GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h; sourceTree = "<group>"; }; - 32CED92C605AFBFFF75E32B97F855D08 /* GULSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULSwizzler.m; path = GoogleUtilities/MethodSwizzler/GULSwizzler.m; sourceTree = "<group>"; }; + 17CCF5036BBF759B3851A291325A58DD /* FIRInstanceIDCheckinPreferences.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinPreferences.h; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences.h; sourceTree = "<group>"; }; + 1911D2DEEADE2AAC254508D0D8D4DD05 /* FIRInstanceIDAuthService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAuthService.h; path = Firebase/InstanceID/FIRInstanceIDAuthService.h; sourceTree = "<group>"; }; + 1A3A538DA0C18DD8A698010361D27A8A /* FIROptionsInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptionsInternal.h; path = Firebase/Core/Private/FIROptionsInternal.h; sourceTree = "<group>"; }; + 1DEDD6F11A6592A96F72E6E90EC1FD73 /* GTLRSheetsService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRSheetsService.m; path = Source/GeneratedServices/Sheets/GTLRSheetsService.m; sourceTree = "<group>"; }; + 2060C1F4AD9CA43E0B2EA2B28F55A284 /* GULNetworkConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkConstants.h; path = GoogleUtilities/Network/Private/GULNetworkConstants.h; sourceTree = "<group>"; }; + 20BB64A0D3C190E922B42A7DABDF4351 /* FIRLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLoggerLevel.h; path = Firebase/Core/Public/FIRLoggerLevel.h; sourceTree = "<group>"; }; + 20CB19971EEB7403662B8D6D969AAA1B /* GTLRSheetsQuery.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRSheetsQuery.m; path = Source/GeneratedServices/Sheets/GTLRSheetsQuery.m; sourceTree = "<group>"; }; + 217305076F6C446D80ECEE650D6FF336 /* GTLRFramework.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRFramework.h; path = Source/Utilities/GTLRFramework.h; sourceTree = "<group>"; }; + 21E1BF177C744299F7CB68726168AE6D /* GULLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULLogger.m; path = GoogleUtilities/Logger/GULLogger.m; sourceTree = "<group>"; }; + 2554DC9B3976244F8B3433B862DD019F /* nanopb-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-prefix.pch"; sourceTree = "<group>"; }; + 26B79195C4C8B553F57B3D3535D34328 /* pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb.h; sourceTree = "<group>"; }; + 26B7B16A2741E588921CA76370A6E0C6 /* FIRComponentType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentType.h; path = Firebase/Core/Private/FIRComponentType.h; sourceTree = "<group>"; }; + 26EEA6C6A294786C4626A5D90B0CC7A8 /* FirebaseInstanceID.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseInstanceID.xcconfig; sourceTree = "<group>"; }; + 2702574391D7BC669EE01BBCB336A64C /* FIRDependency.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRDependency.h; path = Firebase/Core/Private/FIRDependency.h; sourceTree = "<group>"; }; + 271F6CEBA30B3B7191840DEDD47F81FC /* FIRApp.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRApp.m; path = Firebase/Core/FIRApp.m; sourceTree = "<group>"; }; + 2897AC5168624488338884B7C36B5865 /* FIRInstanceIDConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDConstants.m; path = Firebase/InstanceID/FIRInstanceIDConstants.m; sourceTree = "<group>"; }; + 290A38F9B7BEF687F3DBB3C3255CC233 /* FIRInstanceIDKeyPairStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPairStore.h; path = Firebase/InstanceID/FIRInstanceIDKeyPairStore.h; sourceTree = "<group>"; }; + 294D8D5384102F5EC8F36C99BD40B553 /* GULAppEnvironmentUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppEnvironmentUtil.m; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m; sourceTree = "<group>"; }; + 29BA6B4B3A27AAF40CE91D7F460B4C09 /* FIRInstanceIDTokenOperation+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceIDTokenOperation+Private.h"; path = "Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h"; sourceTree = "<group>"; }; + 2B6E82FA3B095C371861788955184AAA /* GULOriginalIMPConvenienceMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULOriginalIMPConvenienceMacros.h; path = GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h; sourceTree = "<group>"; }; + 2CA56DE4B76F14F33BDA8853272D8366 /* GTLRDuration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRDuration.h; path = Source/Objects/GTLRDuration.h; sourceTree = "<group>"; }; + 2CB4C2370A78826FB61E74033B2F9BFF /* GTMSessionFetcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMSessionFetcher.m; path = Source/GTMSessionFetcher.m; sourceTree = "<group>"; }; + 2EDD142F7B8EDBDDDDB5BF548CD9DED8 /* FirebaseInstanceID-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseInstanceID-dummy.m"; sourceTree = "<group>"; }; + 2F3C1179BBE0B2F619AB8D842242DF54 /* GTLRService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRService.h; path = Source/Objects/GTLRService.h; sourceTree = "<group>"; }; + 2F783D0C9291771FE7CD74A47090BDE4 /* pb_encode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_encode.h; sourceTree = "<group>"; }; + 30630E98C2C2879DE2C1248CA2C52F19 /* FIRInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceID.h; path = Firebase/InstanceID/Public/FIRInstanceID.h; sourceTree = "<group>"; }; + 312A54AECBD7450DEF1199ACE70B112A /* FirebaseCore-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseCore-dummy.m"; sourceTree = "<group>"; }; + 321D5E4C31CED38A5BE1C3397595E7F5 /* FIRVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRVersion.m; path = Firebase/Core/FIRVersion.m; sourceTree = "<group>"; }; + 33E015D9CF73817DBAE5EE02250E5DED /* GULLoggerCodes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerCodes.h; path = GoogleUtilities/Common/GULLoggerCodes.h; sourceTree = "<group>"; }; + 347A040A0C9EF78AE6E2CC3305BA6394 /* FIRInstanceIDConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDConstants.h; path = Firebase/InstanceID/FIRInstanceIDConstants.h; sourceTree = "<group>"; }; + 34F74B35ABB197CD426E4B528256BA88 /* FIRInstanceIDTokenDeleteOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenDeleteOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.m; sourceTree = "<group>"; }; 35039373C75F0CAA94D50D97AF90BF12 /* Pods-TeachersAssistantTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TeachersAssistantTests-acknowledgements.markdown"; sourceTree = "<group>"; }; - 36376442373649570509651445F690F2 /* FIRInstanceIDBackupExcludedPlist.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDBackupExcludedPlist.m; path = Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m; sourceTree = "<group>"; }; - 37267C08CB7A9EA152D4AD641442A31A /* GoogleAppMeasurement.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleAppMeasurement.xcconfig; sourceTree = "<group>"; }; - 3922B447A4A2A874121C267E40012CD2 /* FirebaseInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseInstanceID.h; path = Firebase/InstanceID/Public/FirebaseInstanceID.h; sourceTree = "<group>"; }; - 3BFE4C8CE0104CF4C505FE3DF5422F51 /* GULAppDelegateSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppDelegateSwizzler.m; path = GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m; sourceTree = "<group>"; }; - 3C5AB1B9901286B399B7703CDFF3E673 /* GoogleUtilities-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-prefix.pch"; sourceTree = "<group>"; }; - 3CDFE0A10CBBC5A70389A8808D53982E /* FIRConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRConfiguration.h; path = Firebase/Core/Public/FIRConfiguration.h; sourceTree = "<group>"; }; - 3D4B5C9B43DB2526EF6BF314E82C38F1 /* FIRInstanceID+Testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceID+Testing.h"; path = "Firebase/InstanceID/FIRInstanceID+Testing.h"; sourceTree = "<group>"; }; + 357B0ECED3D296D4A9739C22E28FF76D /* FIRVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRVersion.h; path = Firebase/Core/Private/FIRVersion.h; sourceTree = "<group>"; }; + 359D1387EF23CF339959AB5D298D182D /* GoogleAPIClientForREST-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GoogleAPIClientForREST-dummy.m"; sourceTree = "<group>"; }; + 368ACD55DE3CCC50F08A01196D3AAF5D /* Pods_TeachersAssistant.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TeachersAssistant.framework; path = "Pods-TeachersAssistant.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3693350ACB41663D2E80F539D7E5C50E /* FIRInstanceIDVersionUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDVersionUtilities.h; path = Firebase/InstanceID/FIRInstanceIDVersionUtilities.h; sourceTree = "<group>"; }; + 374883FE51263B2BD861B86677BF5ECA /* FIRLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLibrary.h; path = Firebase/Core/Private/FIRLibrary.h; sourceTree = "<group>"; }; + 385F92547477A014104A69ADC304BCAD /* GoogleAPIClientForREST-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleAPIClientForREST-prefix.pch"; sourceTree = "<group>"; }; + 3895CC74E7806861C8DB5D96CD95C007 /* FIRInstanceIDKeyPairUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPairUtilities.m; path = Firebase/InstanceID/FIRInstanceIDKeyPairUtilities.m; sourceTree = "<group>"; }; + 39DE45AADB09BA42C0F26F4A09E7BBEE /* FIRInstanceIDTokenOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenOperation.h; sourceTree = "<group>"; }; + 3AA55CFA88ACE2B99420F368974ED9E7 /* FIRInstanceIDStringEncoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDStringEncoding.h; path = Firebase/InstanceID/FIRInstanceIDStringEncoding.h; sourceTree = "<group>"; }; + 3CAC70B7C1A8906857FA3FC99C1BD4EC /* pb_common.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_common.c; sourceTree = "<group>"; }; 3DFABC77294AE1B3C79891E913990217 /* Pods-TeachersAssistant-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TeachersAssistant-umbrella.h"; sourceTree = "<group>"; }; - 3F162645BE355033F19D49D40BEA124E /* FIRInstanceIDKeyPair.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPair.h; path = Firebase/InstanceID/FIRInstanceIDKeyPair.h; sourceTree = "<group>"; }; - 4242EFC1874D23623622538D952F8B5B /* FIRInstanceIDTokenInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenInfo.h; path = Firebase/InstanceID/FIRInstanceIDTokenInfo.h; sourceTree = "<group>"; }; + 3E8692905552E367BFE89057E3905DB9 /* FIRInstanceIDAuthKeyChain.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAuthKeyChain.h; path = Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h; sourceTree = "<group>"; }; + 4094FA6772FCD62F992E63FD03A145DC /* FIRIMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRIMessageCode.h; path = Firebase/InstanceID/FIRIMessageCode.h; sourceTree = "<group>"; }; + 415002A8AF1CDA669462CD54B22427D1 /* GoogleUtilities-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GoogleUtilities-Info.plist"; sourceTree = "<group>"; }; + 41F647164C5CA7901E4BFE7741255A55 /* nanopb-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "nanopb-dummy.m"; sourceTree = "<group>"; }; + 420B98A22418457646F0C81E28AEC1F8 /* GoogleUtilities-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-prefix.pch"; sourceTree = "<group>"; }; + 4370B2DFACB00E7DEC0868A68AA87DBD /* GTMMIMEDocument.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMMIMEDocument.m; path = Source/GTMMIMEDocument.m; sourceTree = "<group>"; }; + 43D7AEB5BE9D344529D075789E88E926 /* FIRInstanceIDLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDLogger.h; path = Firebase/InstanceID/FIRInstanceIDLogger.h; sourceTree = "<group>"; }; 44581CF76B50941A6B5755E48A3F0B73 /* Pods-TeachersAssistantTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-TeachersAssistantTests.modulemap"; sourceTree = "<group>"; }; - 49168AFB20C78AD0A895694EF3D63CC4 /* GoogleUtilities.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUtilities.xcconfig; sourceTree = "<group>"; }; - 4E52F5018FDC1CBA40A1B2AF3701A7FA /* GULNetworkURLSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkURLSession.m; path = GoogleUtilities/Network/GULNetworkURLSession.m; sourceTree = "<group>"; }; - 4FD50293995AD747738A754E700980D2 /* FIRInstanceIDKeyPair.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPair.m; path = Firebase/InstanceID/FIRInstanceIDKeyPair.m; sourceTree = "<group>"; }; - 5089053A2D4D7A353FEF844FC3C7DC8F /* Pods_TeachersAssistant.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TeachersAssistant.framework; path = "Pods-TeachersAssistant.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 538DA2323E03A0DB8EB8C32F053A1AA9 /* FirebaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCore.framework; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5471452A72F4611E3232627BC06ADD9A /* GULNetworkMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkMessageCode.h; path = GoogleUtilities/Network/Private/GULNetworkMessageCode.h; sourceTree = "<group>"; }; - 55866B95A313509A4206813BC4B48CD6 /* FIRInstanceID+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceID+Private.h"; path = "Firebase/InstanceID/FIRInstanceID+Private.h"; sourceTree = "<group>"; }; - 57D0D5C4A8C0DE64D2AC9920D785D32E /* FIRLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLoggerLevel.h; path = Firebase/Core/Public/FIRLoggerLevel.h; sourceTree = "<group>"; }; - 58F0868863F06C7865D23136C4091147 /* FIRInstanceIDTokenDeleteOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenDeleteOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.m; sourceTree = "<group>"; }; - 5B956219AB913B55F60DC376E29FADC1 /* GULOriginalIMPConvenienceMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULOriginalIMPConvenienceMacros.h; path = GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h; sourceTree = "<group>"; }; - 5C49A18BA48C07644DDE0215A595578E /* FirebaseInstanceID.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseInstanceID.xcconfig; sourceTree = "<group>"; }; - 5C905C3155AA4CAC5E7249613453764B /* FIRAnalyticsConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAnalyticsConfiguration.h; path = Firebase/Core/Public/FIRAnalyticsConfiguration.h; sourceTree = "<group>"; }; - 5C91CF94C6994C814D43E3FCD79444A0 /* FIRAppInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppInternal.h; path = Firebase/Core/Private/FIRAppInternal.h; sourceTree = "<group>"; }; - 600DA0E155B6B8E031A9B0DB8F58EC5F /* FIRInstanceIDStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDStore.m; path = Firebase/InstanceID/FIRInstanceIDStore.m; sourceTree = "<group>"; }; - 6158927EA607D8FEFA5317569E679441 /* FIRComponentType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentType.h; path = Firebase/Core/Private/FIRComponentType.h; sourceTree = "<group>"; }; - 639A8B82A185BC312DC3B6CAF5A62F01 /* FIRBundleUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRBundleUtil.h; path = Firebase/Core/Private/FIRBundleUtil.h; sourceTree = "<group>"; }; - 63F5C9FB083CECFE9AD2689CAFED23FE /* pb_decode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_decode.h; sourceTree = "<group>"; }; - 64B7AC5ED456E5A4AB1B41E61E53FF71 /* FIRInstanceIDTokenStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenStore.h; path = Firebase/InstanceID/FIRInstanceIDTokenStore.h; sourceTree = "<group>"; }; - 6519211EB05611CA463B38D0D316C570 /* nanopb-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "nanopb-Info.plist"; sourceTree = "<group>"; }; - 65446BC662392EE8067E81346AB6D2E0 /* GULAppDelegateSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler.h; path = GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h; sourceTree = "<group>"; }; - 656A5D59CF895DF019000577CFBE409D /* FIRComponentContainerInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainerInternal.h; path = Firebase/Core/Private/FIRComponentContainerInternal.h; sourceTree = "<group>"; }; - 66D59167D31E9AB0B577C1597DE53BDF /* FIROptionsInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptionsInternal.h; path = Firebase/Core/Private/FIROptionsInternal.h; sourceTree = "<group>"; }; - 68F3D9F0F4CB406856DE551E00F4BA94 /* FIRInstanceIDCheckinPreferences+Internal.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FIRInstanceIDCheckinPreferences+Internal.m"; path = "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.m"; sourceTree = "<group>"; }; - 69D06FAE028081AF31F2CC18622AEB9B /* FIRInstanceIDStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDStore.h; path = Firebase/InstanceID/FIRInstanceIDStore.h; sourceTree = "<group>"; }; - 6A0A7EDEA8C34882ED9F21855FFD6B54 /* FIRInstanceIDVersionUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDVersionUtilities.m; path = Firebase/InstanceID/FIRInstanceIDVersionUtilities.m; sourceTree = "<group>"; }; - 6A72A94599CE16306189626A8244DB0B /* FirebaseInstanceID.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseInstanceID.framework; path = FirebaseInstanceID.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6C897EBDA102E414BA5982CD110CF5EA /* GoogleUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleUtilities.framework; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6CBD9DE48945BE5D8D3F6DF83BCAEBB7 /* FIRInstanceIDURLQueryItem.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDURLQueryItem.m; path = Firebase/InstanceID/FIRInstanceIDURLQueryItem.m; sourceTree = "<group>"; }; + 44BEC70CEE7D3F3A73A2AAFC74050F84 /* Pods_TeachersAssistantTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TeachersAssistantTests.framework; path = "Pods-TeachersAssistantTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4673860D113355AAD052F21D1D4399F7 /* GTLRErrorObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRErrorObject.m; path = Source/Objects/GTLRErrorObject.m; sourceTree = "<group>"; }; + 499E59FADC4F9A4FD57637B40A7E0AEB /* FIRComponent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponent.h; path = Firebase/Core/Private/FIRComponent.h; sourceTree = "<group>"; }; + 49C0CD9206E359DF82CB5D71E5B5097F /* FIRInstanceID.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceID.m; path = Firebase/InstanceID/FIRInstanceID.m; sourceTree = "<group>"; }; + 4AAFBE6129D7F7EE3751814049C55310 /* FIRApp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRApp.h; path = Firebase/Core/Public/FIRApp.h; sourceTree = "<group>"; }; + 4B6FFA90600425E042D0DB9FE2BCF44F /* GTLRURITemplate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRURITemplate.m; path = Source/Utilities/GTLRURITemplate.m; sourceTree = "<group>"; }; + 4BBAFAC2A9A529CD28ED16BCF13AECE4 /* GTMSessionFetcherService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMSessionFetcherService.m; path = Source/GTMSessionFetcherService.m; sourceTree = "<group>"; }; + 4CF4F8A5BE53C6A071CEA011E4022665 /* FIRInstanceIDKeychain.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeychain.m; path = Firebase/InstanceID/FIRInstanceIDKeychain.m; sourceTree = "<group>"; }; + 4D669732E0BA0105016B0BFAF2C9EEE1 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; + 4DF7D23FF6ED5F29179DAA124D6139E0 /* nanopb.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = nanopb.modulemap; sourceTree = "<group>"; }; + 4E3ADB623A71EF1040F07F4474B13C1F /* FIRComponentContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentContainer.m; path = Firebase/Core/FIRComponentContainer.m; sourceTree = "<group>"; }; + 4E8A41DFF31FE019B30BF0B9770BE14D /* GTMSessionUploadFetcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMSessionUploadFetcher.h; path = Source/GTMSessionUploadFetcher.h; sourceTree = "<group>"; }; + 500B9CE6A2B5ECC69A261EE855D14BDC /* FIRInstanceIDKeyPairUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPairUtilities.h; path = Firebase/InstanceID/FIRInstanceIDKeyPairUtilities.h; sourceTree = "<group>"; }; + 504D2264E8585EE42A61DCE121FA7334 /* GTMSessionFetcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMSessionFetcher.h; path = Source/GTMSessionFetcher.h; sourceTree = "<group>"; }; + 516450B002D7F6CFAD2116CE501F57A3 /* FIRInstanceIDBackupExcludedPlist.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDBackupExcludedPlist.m; path = Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m; sourceTree = "<group>"; }; + 52C6C4BA62B48087FCEC1011C608BDBA /* FIRInstanceID+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceID+Private.h"; path = "Firebase/InstanceID/FIRInstanceID+Private.h"; sourceTree = "<group>"; }; + 5422968193C250477308AB011920863A /* GULReachabilityChecker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULReachabilityChecker.m; path = GoogleUtilities/Reachability/GULReachabilityChecker.m; sourceTree = "<group>"; }; + 547B4BA4FC4AC94B80BCCBAA38BEEB88 /* GTMReadMonitorInputStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMReadMonitorInputStream.m; path = Source/GTMReadMonitorInputStream.m; sourceTree = "<group>"; }; + 54C93ABAE1D2705DC66F2D99C41F09C5 /* NSError+FIRInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+FIRInstanceID.h"; path = "Firebase/InstanceID/NSError+FIRInstanceID.h"; sourceTree = "<group>"; }; + 54F922D095E5B9A857BABDE9A29D2EA7 /* GTLRQuery.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRQuery.m; path = Source/Objects/GTLRQuery.m; sourceTree = "<group>"; }; + 559A7AF75C7825A882D221F994EDE0FE /* FIRInstanceIDStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDStore.h; path = Firebase/InstanceID/FIRInstanceIDStore.h; sourceTree = "<group>"; }; + 56CE98F133AB2BA1382EB2FAB97B0AF8 /* GTLRDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRDefines.h; path = Source/GTLRDefines.h; sourceTree = "<group>"; }; + 571008D58CCCDD41BFC7992FC649BC4D /* FIRInstanceIDTokenOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenOperation.m; sourceTree = "<group>"; }; + 580320C3A739577369ED81033BFCE123 /* FIRInstanceIDAPNSInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAPNSInfo.m; path = Firebase/InstanceID/FIRInstanceIDAPNSInfo.m; sourceTree = "<group>"; }; + 583BBC1D30E637FD9C0BB1442D90C0D2 /* FirebaseInstanceID.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseInstanceID.framework; path = FirebaseInstanceID.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 58AC66E22BF3126CF2AB5A768FAABD0F /* GULLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerLevel.h; path = GoogleUtilities/Logger/Public/GULLoggerLevel.h; sourceTree = "<group>"; }; + 5AA8AEFC53E239B1C44121C6399BDF14 /* FirebaseCore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseCore.h; path = Firebase/Core/Public/FirebaseCore.h; sourceTree = "<group>"; }; + 5B0CAE77A1601E438C553BCD88A71A50 /* GTMSessionFetcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GTMSessionFetcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5BC9D2775C1E7A3C43A2D9FB68FD6140 /* GTMGatherInputStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMGatherInputStream.h; path = Source/GTMGatherInputStream.h; sourceTree = "<group>"; }; + 5D066DD9A210B03A0B9E0DAB7683DE7B /* nanopb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = nanopb.framework; path = nanopb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D74E330C10A59ECB0E3A651183B5902 /* FIRAnalyticsConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAnalyticsConfiguration.m; path = Firebase/Core/FIRAnalyticsConfiguration.m; sourceTree = "<group>"; }; + 5E92A8BA31159E11D52BAF17FD3D11F2 /* GTLRUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRUtilities.m; path = Source/Utilities/GTLRUtilities.m; sourceTree = "<group>"; }; + 6004EF0F526D87CB1DB9CE9FE43FBE40 /* GTLRUploadParameters.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRUploadParameters.m; path = Source/Objects/GTLRUploadParameters.m; sourceTree = "<group>"; }; + 61C9D32696A6761D07B73E8E6A1B0603 /* FIRInstanceIDStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDStore.m; path = Firebase/InstanceID/FIRInstanceIDStore.m; sourceTree = "<group>"; }; + 632CEDCB8B1B6CADF8FA17EE175367B8 /* GTLRBase64.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRBase64.h; path = Source/Utilities/GTLRBase64.h; sourceTree = "<group>"; }; + 637245938FD074F3B9AE443C80D0A34E /* FIRErrorCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrorCode.h; path = Firebase/Core/Private/FIRErrorCode.h; sourceTree = "<group>"; }; + 63CA82E29BA583AD0A52D7A37558E2F9 /* FIRInstanceIDKeyPairStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPairStore.m; path = Firebase/InstanceID/FIRInstanceIDKeyPairStore.m; sourceTree = "<group>"; }; + 64A59B0655E40448F960D448BFE2B4D4 /* GULAppDelegateSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppDelegateSwizzler.m; path = GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m; sourceTree = "<group>"; }; + 66B1B5E0F02389648F7D7F08EE92449E /* FirebaseCore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseCore.xcconfig; sourceTree = "<group>"; }; + 6924954BD2F30993FB8230C4F0B6A5F5 /* GULNetworkURLSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkURLSession.m; path = GoogleUtilities/Network/GULNetworkURLSession.m; sourceTree = "<group>"; }; + 6980A1AC965054D472E7A754E0433A29 /* FIRInstanceIDTokenFetchOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenFetchOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m; sourceTree = "<group>"; }; + 6BC4FA64AE2079B4E811849A1860A4A6 /* FIRInstanceIDDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDDefines.h; path = Firebase/InstanceID/FIRInstanceIDDefines.h; sourceTree = "<group>"; }; + 6C217B0FC3899164615FD61D788E7FBA /* pb_encode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_encode.c; sourceTree = "<group>"; }; + 6C7078467D0A33F3EBEFE639202A278A /* GULAppEnvironmentUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppEnvironmentUtil.h; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h; sourceTree = "<group>"; }; 6CBEAF0154F92F0CD942C90DCF6C7A1B /* Pods-TeachersAssistantTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TeachersAssistantTests-Info.plist"; sourceTree = "<group>"; }; 6CF6CDA458E493AD3A13C09451C84F01 /* Pods-TeachersAssistant-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TeachersAssistant-acknowledgements.markdown"; sourceTree = "<group>"; }; - 6D51E316195287C40904DFCF52B1B8B3 /* FIRInstanceIDCheckinStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinStore.h; path = Firebase/InstanceID/FIRInstanceIDCheckinStore.h; sourceTree = "<group>"; }; - 6DE9D4D09D58B6CC296CC31B28C71A68 /* GULMutableDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULMutableDictionary.h; path = GoogleUtilities/Network/Private/GULMutableDictionary.h; sourceTree = "<group>"; }; - 6DED144D39D4BC1B72D7492161128B2E /* FIRInstanceIDAuthKeyChain.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAuthKeyChain.h; path = Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h; sourceTree = "<group>"; }; - 6E083BECC4ABE126127F7F318F3981A7 /* FIRAnalyticsConnector.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FIRAnalyticsConnector.framework; path = Frameworks/FIRAnalyticsConnector.framework; sourceTree = "<group>"; }; - 6E439097F5D3E57E08E7B5A0CF53E344 /* FIRInstanceIDTokenFetchOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenFetchOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h; sourceTree = "<group>"; }; - 7397308853202DF815AD5A1CD1C07E45 /* FIRInstanceIDKeyPairUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPairUtilities.m; path = Firebase/InstanceID/FIRInstanceIDKeyPairUtilities.m; sourceTree = "<group>"; }; + 6DE4621B5BF4A7F747C7A66937E79855 /* FIRLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLogger.h; path = Firebase/Core/Private/FIRLogger.h; sourceTree = "<group>"; }; + 6DF78EC6817986F64D37F229896D32E8 /* FIRInstanceIDTokenStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenStore.m; path = Firebase/InstanceID/FIRInstanceIDTokenStore.m; sourceTree = "<group>"; }; + 6F130B340A708E32CAEA83F0B6619B08 /* GULNetworkMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkMessageCode.h; path = GoogleUtilities/Network/Private/GULNetworkMessageCode.h; sourceTree = "<group>"; }; + 7110D63F53E9A413E26235ED20E973C9 /* FirebaseCore-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCore-umbrella.h"; sourceTree = "<group>"; }; + 72A68E1354B8F68044AB4DD21E274D55 /* FIRComponentContainerInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainerInternal.h; path = Firebase/Core/Private/FIRComponentContainerInternal.h; sourceTree = "<group>"; }; + 734E5DCCCE857FFA34745EB5C2D6C5EA /* pb_decode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_decode.h; sourceTree = "<group>"; }; + 73DEB6CDF7421F78C01F7474A67404F3 /* GTLRBatchQuery.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRBatchQuery.m; path = Source/Objects/GTLRBatchQuery.m; sourceTree = "<group>"; }; 74104373A68924B948E2FD66656DDF04 /* Pods-TeachersAssistant-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TeachersAssistant-dummy.m"; sourceTree = "<group>"; }; - 74804560F99741B16093413E3ED271A1 /* FIRInstanceIDCheckinPreferences_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinPreferences_Private.h; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h; sourceTree = "<group>"; }; - 76F8720EE48E3A1D2085F0BC9F06CEE5 /* FIRInstanceIDVersionUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDVersionUtilities.h; path = Firebase/InstanceID/FIRInstanceIDVersionUtilities.h; sourceTree = "<group>"; }; - 7847D97A17737D2D6CB590B13CE564CC /* FIRConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRConfiguration.m; path = Firebase/Core/FIRConfiguration.m; sourceTree = "<group>"; }; - 79BA4CC3A4E8659F47EECDA56535E22C /* FirebaseAnalytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseAnalytics.framework; path = Frameworks/FirebaseAnalytics.framework; sourceTree = "<group>"; }; - 7DB356CAA2B666246D87D8FC92ABD8F2 /* nanopb.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = nanopb.modulemap; sourceTree = "<group>"; }; - 807CA56F6A01FAE1DD7905DCD950E8CB /* GULLoggerCodes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerCodes.h; path = GoogleUtilities/Common/GULLoggerCodes.h; sourceTree = "<group>"; }; + 745F466B0E0CFD0057D71D440A6D7D4D /* FIRLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRLogger.m; path = Firebase/Core/FIRLogger.m; sourceTree = "<group>"; }; + 74EF9C835171D35F298E15ACA4B38506 /* GTLRBatchQuery.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRBatchQuery.h; path = Source/Objects/GTLRBatchQuery.h; sourceTree = "<group>"; }; + 74F65B52CC04DEB89684D01BB66C6E41 /* GULNetworkConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkConstants.m; path = GoogleUtilities/Network/GULNetworkConstants.m; sourceTree = "<group>"; }; + 7943A53065BB0DF789C3CDEEC1004CD9 /* FIRInstanceIDKeychain.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeychain.h; path = Firebase/InstanceID/FIRInstanceIDKeychain.h; sourceTree = "<group>"; }; + 7AF84FD306398BAAB32D5EB452C1E569 /* GTLRRuntimeCommon.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRRuntimeCommon.h; path = Source/Objects/GTLRRuntimeCommon.h; sourceTree = "<group>"; }; + 7C62C59F592BBF3BA1637FDA859BE9A6 /* FIROptions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptions.h; path = Firebase/Core/Public/FIROptions.h; sourceTree = "<group>"; }; + 7CC67CCBC2D46F79CC57C3F798122C7E /* FIRComponentType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentType.m; path = Firebase/Core/FIRComponentType.m; sourceTree = "<group>"; }; + 7CCAEF0B1870F052EDE7D124EE35E5F2 /* GULReachabilityChecker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityChecker.h; path = GoogleUtilities/Reachability/Private/GULReachabilityChecker.h; sourceTree = "<group>"; }; + 7D8784AB13BF6FBDE11552EE49C12DCC /* FIRAnalyticsConnector.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FIRAnalyticsConnector.framework; path = Frameworks/FIRAnalyticsConnector.framework; sourceTree = "<group>"; }; + 7DA698D652FCFAC16C9EEFCA83C85E72 /* GULReachabilityMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityMessageCode.h; path = GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h; sourceTree = "<group>"; }; + 7ECD67C851A9B1AAF8CB0A1D65A67194 /* GoogleAPIClientForREST-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GoogleAPIClientForREST-Info.plist"; sourceTree = "<group>"; }; + 7FBB2D1B3717FA3F20CC83DA53908482 /* FIRInstanceIDKeyPair.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeyPair.m; path = Firebase/InstanceID/FIRInstanceIDKeyPair.m; sourceTree = "<group>"; }; 80D0ABF6F63F3EF080A906F8C625412E /* Pods-TeachersAssistant.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TeachersAssistant.debug.xcconfig"; sourceTree = "<group>"; }; - 828E16FFF2FF45FB2837CBB01DC1F03A /* Pods_TeachersAssistantTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TeachersAssistantTests.framework; path = "Pods-TeachersAssistantTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 84E1E0BB483A5ED582BCF730EFC79223 /* nanopb-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-umbrella.h"; sourceTree = "<group>"; }; - 87278A4E974DA21F7BA3EE74DE03DADE /* FIRInstanceIDLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDLogger.h; path = Firebase/InstanceID/FIRInstanceIDLogger.h; sourceTree = "<group>"; }; - 87F87803FFB82959EC1D3D60F01B0458 /* FIRInstanceIDLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDLogger.m; path = Firebase/InstanceID/FIRInstanceIDLogger.m; sourceTree = "<group>"; }; - 8B55F7049963BD05146CEAF24E766A28 /* FIRComponentContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainer.h; path = Firebase/Core/Private/FIRComponentContainer.h; sourceTree = "<group>"; }; - 8BC45FD590053D7BF61B9B75DB594D11 /* FIRInstanceIDUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDUtilities.m; path = Firebase/InstanceID/FIRInstanceIDUtilities.m; sourceTree = "<group>"; }; - 8BE349DEB3CC69BD666C0E30A25A632E /* GULLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerLevel.h; path = GoogleUtilities/Logger/Public/GULLoggerLevel.h; sourceTree = "<group>"; }; - 8CFE5D0397A33F3BB0D159D063A0730D /* GULSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSwizzler.h; path = GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h; sourceTree = "<group>"; }; - 8D8E654970A93A22D15B4DB3F736820B /* pb_decode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_decode.c; sourceTree = "<group>"; }; - 90D2864C001A2BBBDC9E2870EE0A692A /* FIRInstanceIDTokenDeleteOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenDeleteOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h; sourceTree = "<group>"; }; - 923510B47157D70AE61469BD0DB53003 /* GULAppEnvironmentUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppEnvironmentUtil.h; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h; sourceTree = "<group>"; }; - 933E7CB41F265BF8575D8E6571D4F745 /* pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb.h; sourceTree = "<group>"; }; - 94F3B242B6FA54A454F2784A59455D37 /* FIRInstanceIDCheckinStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinStore.m; path = Firebase/InstanceID/FIRInstanceIDCheckinStore.m; sourceTree = "<group>"; }; - 95A030E997930B42AFCBB55C290C6DC7 /* GULNetwork.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetwork.m; path = GoogleUtilities/Network/GULNetwork.m; sourceTree = "<group>"; }; - 95A4EBC5E3FF3C286D3DCF3C04A8320A /* nanopb-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-prefix.pch"; sourceTree = "<group>"; }; - 975AEB856E53CB32B52EE02D1BC7D00C /* GULUserDefaults.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULUserDefaults.m; path = GoogleUtilities/UserDefaults/GULUserDefaults.m; sourceTree = "<group>"; }; - 9A1269954F868B70BE772E5B5D20024F /* FirebaseCore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseCore.h; path = Firebase/Core/Public/FirebaseCore.h; sourceTree = "<group>"; }; - 9A4F2F1F710FE4FF3A1ACDAB7711391D /* FirebaseInstanceID-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseInstanceID-umbrella.h"; sourceTree = "<group>"; }; - 9AF42147C59B306D29A7697CF513F604 /* FIRAnalyticsConfiguration+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRAnalyticsConfiguration+Internal.h"; path = "Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h"; sourceTree = "<group>"; }; - 9BAC6A2CCC656481599672B6E990A5BD /* FIRInstanceIDTokenOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenOperation.h; sourceTree = "<group>"; }; - 9BDD351D126C991113400163846774B8 /* FIRDependency.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRDependency.m; path = Firebase/Core/FIRDependency.m; sourceTree = "<group>"; }; - 9D1936355E3B3A5466062133CD40EE0E /* FIROptions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptions.h; path = Firebase/Core/Public/FIROptions.h; sourceTree = "<group>"; }; + 81BAD96EB0AFAC3A8B603522A2FD846D /* GoogleUtilities.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUtilities.xcconfig; sourceTree = "<group>"; }; + 82BB2879569D536A6530B687161DC8B1 /* FIRConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRConfiguration.m; path = Firebase/Core/FIRConfiguration.m; sourceTree = "<group>"; }; + 83537337655F0E50082CD7E2603EFCCA /* GULMutableDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULMutableDictionary.h; path = GoogleUtilities/Network/Private/GULMutableDictionary.h; sourceTree = "<group>"; }; + 83DF78C7972D2E89918296EE5DA73881 /* FirebaseCoreDiagnostics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseCoreDiagnostics.framework; path = Frameworks/FirebaseCoreDiagnostics.framework; sourceTree = "<group>"; }; + 8416808131C45D6E3E6BC32C40C76A82 /* GoogleAppMeasurement.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleAppMeasurement.xcconfig; sourceTree = "<group>"; }; + 84B75F5A2DD728216A8F32ED58EB0EEC /* GoogleUtilities-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-umbrella.h"; sourceTree = "<group>"; }; + 85599DEC750671534D9582042B8D286B /* FIRInstanceIDTokenStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenStore.h; path = Firebase/InstanceID/FIRInstanceIDTokenStore.h; sourceTree = "<group>"; }; + 87191E9BA56B22A703AD591A7963F5E3 /* FirebaseInstanceID-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseInstanceID-umbrella.h"; sourceTree = "<group>"; }; + 8795560D80511CF84F3EF6325D2A8F51 /* GTMReadMonitorInputStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMReadMonitorInputStream.h; path = Source/GTMReadMonitorInputStream.h; sourceTree = "<group>"; }; + 88278CB0E802158640AF8B0E24FAD79A /* GTMMIMEDocument.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMMIMEDocument.h; path = Source/GTMMIMEDocument.h; sourceTree = "<group>"; }; + 8A9E981E17F06280BA9E8358D8FB90FD /* GTMSessionFetcherLogging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMSessionFetcherLogging.h; path = Source/GTMSessionFetcherLogging.h; sourceTree = "<group>"; }; + 8AB3ECC077B85578CA86B1AB55A7B12D /* GoogleAPIClientForREST-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleAPIClientForREST-umbrella.h"; sourceTree = "<group>"; }; + 8AC4E56443368A59F770E05990328FBB /* FIRComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponent.m; path = Firebase/Core/FIRComponent.m; sourceTree = "<group>"; }; + 8AF638AC9C581DA7EAF1350F43C6E347 /* FIRComponentContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainer.h; path = Firebase/Core/Private/FIRComponentContainer.h; sourceTree = "<group>"; }; + 8CA5A0BB0A7F36E8F7189D91C8B29728 /* FIRErrors.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrors.h; path = Firebase/Core/Private/FIRErrors.h; sourceTree = "<group>"; }; + 8F06F1782AC0D2EF8E359A25024009A7 /* FIRInstanceIDAuthKeyChain.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAuthKeyChain.m; path = Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m; sourceTree = "<group>"; }; + 8F7812E7B55B6200DF820251D9207F56 /* FIRInstanceIDTokenManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenManager.h; path = Firebase/InstanceID/FIRInstanceIDTokenManager.h; sourceTree = "<group>"; }; + 91FD3DD45495B59CF1AD18C2F1C5664A /* GTLRQuery.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRQuery.h; path = Source/Objects/GTLRQuery.h; sourceTree = "<group>"; }; + 93FB1D3F1A434DADF7E8F2E0DCDF6CB4 /* FIRInstanceIDTokenInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenInfo.m; path = Firebase/InstanceID/FIRInstanceIDTokenInfo.m; sourceTree = "<group>"; }; + 9C00A42C8667CADDFB41282D26A16227 /* nanopb-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-umbrella.h"; sourceTree = "<group>"; }; + 9C252AD090AF9844AB7721AC0D603C74 /* FIRInstanceIDUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDUtilities.h; path = Firebase/InstanceID/FIRInstanceIDUtilities.h; sourceTree = "<group>"; }; 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 9EFC3E9C2F0E1ABA696F73E63B829CE6 /* GULReachabilityChecker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULReachabilityChecker.m; path = GoogleUtilities/Reachability/GULReachabilityChecker.m; sourceTree = "<group>"; }; - A01DF1FAEB9BDFCF3637999217E24CD7 /* GoogleUtilities.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GoogleUtilities.modulemap; sourceTree = "<group>"; }; - A0802C848647300E14D9CEAF6F586E3D /* GULNetworkLoggerProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkLoggerProtocol.h; path = GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h; sourceTree = "<group>"; }; - A14A4950DB0EDF3B8A678040F6D096DB /* FirebaseCore-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseCore-dummy.m"; sourceTree = "<group>"; }; - A1828B5340482EC8D3397627ACC5AB34 /* FIROptions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIROptions.m; path = Firebase/Core/FIROptions.m; sourceTree = "<group>"; }; - A183E0073F3551A0DFFBAD1EE4650667 /* pb_encode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_encode.c; sourceTree = "<group>"; }; - A19E92882D90BF25AF701FA415D5168C /* FIRInstanceIDBackupExcludedPlist.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDBackupExcludedPlist.h; path = Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h; sourceTree = "<group>"; }; - A3E64E6865163D74A4A9C1720F021176 /* GULLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLogger.h; path = GoogleUtilities/Logger/Private/GULLogger.h; sourceTree = "<group>"; }; - A3FE1AA32CB9692E06D6A3C4001459B8 /* FIRInstanceIDTokenInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenInfo.m; path = Firebase/InstanceID/FIRInstanceIDTokenInfo.m; sourceTree = "<group>"; }; + 9E00DA041CCC31B6AEC875C52444A935 /* GULReachabilityChecker+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULReachabilityChecker+Internal.h"; path = "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"; sourceTree = "<group>"; }; + 9F12DC4DF7E7F8A371738879B374FFF5 /* GTLRFramework.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRFramework.m; path = Source/Utilities/GTLRFramework.m; sourceTree = "<group>"; }; + A12B17E00FB8FCB54E94D10975B5302C /* GTLRBase64.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRBase64.m; path = Source/Utilities/GTLRBase64.m; sourceTree = "<group>"; }; + A2672AB4665EB890F1F2E052DC068B0D /* GULNetwork.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetwork.h; path = GoogleUtilities/Network/Private/GULNetwork.h; sourceTree = "<group>"; }; + A36649E2942CA43CF746CB8BB3356A9D /* FIRInstanceIDAuthService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAuthService.m; path = Firebase/InstanceID/FIRInstanceIDAuthService.m; sourceTree = "<group>"; }; + A3EAF3AA8E5BADD73BCE0D2B8CC69B98 /* GULAppDelegateSwizzler_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler_Private.h; path = GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h; sourceTree = "<group>"; }; A40C263DBAD4C39D2E09F730467D5085 /* Pods-TeachersAssistantTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TeachersAssistantTests.debug.xcconfig"; sourceTree = "<group>"; }; A61A5FBB4C4917E85820D47EA05A7A31 /* Pods-TeachersAssistant.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TeachersAssistant.release.xcconfig"; sourceTree = "<group>"; }; - A6BA35651106407F616D7A57FE9D73D3 /* GULNetworkConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkConstants.m; path = GoogleUtilities/Network/GULNetworkConstants.m; sourceTree = "<group>"; }; - A8182678D4ABD0F98642AD7406D1BCB2 /* GoogleUtilities-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-umbrella.h"; sourceTree = "<group>"; }; - AB268DEC9599640A23DDA856C934C565 /* GoogleUtilities-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GoogleUtilities-Info.plist"; sourceTree = "<group>"; }; - ABA62F0D7466C8A6A84592F640B7D0E1 /* FIRComponentType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentType.m; path = Firebase/Core/FIRComponentType.m; sourceTree = "<group>"; }; - ABE610632F28D152955ACE0CE2F28F03 /* FIRAppAssociationRegistration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAppAssociationRegistration.m; path = Firebase/Core/FIRAppAssociationRegistration.m; sourceTree = "<group>"; }; - AD20E02685107AFBC498147B840A6226 /* GoogleUtilities-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GoogleUtilities-dummy.m"; sourceTree = "<group>"; }; - AD786D532408C86D1EB6C927B0C87A75 /* FIRInstanceIDCheckinService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinService.m; path = Firebase/InstanceID/FIRInstanceIDCheckinService.m; sourceTree = "<group>"; }; - AE3708AB69977852B8EC58F245468A62 /* FIRLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRLogger.m; path = Firebase/Core/FIRLogger.m; sourceTree = "<group>"; }; - AE892269DC64E59580E85799336B0E3F /* pb_common.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_common.c; sourceTree = "<group>"; }; - AFF19F90C766B8C2B9A22DE86B1BFC0F /* FirebaseCore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseCore.xcconfig; sourceTree = "<group>"; }; - B04DBDA8123DF97BF5CEBC0B71C46B33 /* FIRLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLibrary.h; path = Firebase/Core/Private/FIRLibrary.h; sourceTree = "<group>"; }; - B3DB5DA6985094E9B258EF0B816C7BAC /* FIRApp.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRApp.m; path = Firebase/Core/FIRApp.m; sourceTree = "<group>"; }; + A62C33CAAD941D27993CFB927857A476 /* FIRAppAssociationRegistration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppAssociationRegistration.h; path = Firebase/Core/Private/FIRAppAssociationRegistration.h; sourceTree = "<group>"; }; + A6EFE17E8C1D2435651AA1CF3C3314CB /* FIRInstanceIDCheckinPreferences+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceIDCheckinPreferences+Internal.h"; path = "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h"; sourceTree = "<group>"; }; + A737C343B6C79B154315978701F29A04 /* FIRInstanceIDLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDLogger.m; path = Firebase/InstanceID/FIRInstanceIDLogger.m; sourceTree = "<group>"; }; + A8CEB8453EFC3EBB8CC7F816596220DD /* GULNetworkURLSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkURLSession.h; path = GoogleUtilities/Network/Private/GULNetworkURLSession.h; sourceTree = "<group>"; }; + AD26A272AD866767EC8863C068DAC7A9 /* FirebaseInstanceID-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "FirebaseInstanceID-Info.plist"; sourceTree = "<group>"; }; + AD9455B83D23BDB4915ED6A130D1C8C4 /* GTMSessionUploadFetcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMSessionUploadFetcher.m; path = Source/GTMSessionUploadFetcher.m; sourceTree = "<group>"; }; + ADB38B25ABA9170879CD55B53412DFE6 /* GTLRBatchResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRBatchResult.h; path = Source/Objects/GTLRBatchResult.h; sourceTree = "<group>"; }; + ADDFACA311CA984C4ED942D7153C7BBC /* GoogleAppMeasurement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleAppMeasurement.framework; path = Frameworks/GoogleAppMeasurement.framework; sourceTree = "<group>"; }; + ADF1B1D4D72635AB6E8D3C36D8D89A6C /* nanopb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = nanopb.xcconfig; sourceTree = "<group>"; }; + B17C169B2ADA959DE38DAE86D07B7379 /* GTLRSheetsObjects.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRSheetsObjects.m; path = Source/GeneratedServices/Sheets/GTLRSheetsObjects.m; sourceTree = "<group>"; }; + B240966BF419D8B916096F61465022D1 /* GoogleAPIClientForREST.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GoogleAPIClientForREST.modulemap; sourceTree = "<group>"; }; + B2CE9C8D3C9509E03742DAAC7D484973 /* FIRInstanceIDCheckinPreferences.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinPreferences.m; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences.m; sourceTree = "<group>"; }; B50337BA0606A576144D70B0E05ED8C1 /* Pods-TeachersAssistant-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TeachersAssistant-Info.plist"; sourceTree = "<group>"; }; - B56AAB2892E7D78A490ACD5C6E90A87F /* FIRInstanceIDTokenOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenOperation.m; sourceTree = "<group>"; }; - B61D10491AFFFCEF1A08FB83E4F44A0A /* pb_encode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_encode.h; sourceTree = "<group>"; }; - B6677173DD856010DD44F240491627F5 /* FIRInstanceIDConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDConstants.m; path = Firebase/InstanceID/FIRInstanceIDConstants.m; sourceTree = "<group>"; }; - B70A1D762675BEC99064CE31C0656656 /* NSError+FIRInstanceID.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+FIRInstanceID.m"; path = "Firebase/InstanceID/NSError+FIRInstanceID.m"; sourceTree = "<group>"; }; - B754517F46C50C45E8EA76654D73524D /* FIRInstanceIDTokenFetchOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenFetchOperation.m; path = Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m; sourceTree = "<group>"; }; - B7B6121D361EE7F4909DE2D1943F322B /* GoogleAppMeasurement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleAppMeasurement.framework; path = Frameworks/GoogleAppMeasurement.framework; sourceTree = "<group>"; }; - B90EE6D4D7C9ADED356D925B56CACE30 /* Firebase.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Firebase.xcconfig; sourceTree = "<group>"; }; - BA3D88362D7798228C9653A26168D35E /* GULNSData+zlib.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "GULNSData+zlib.m"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.m"; sourceTree = "<group>"; }; - BC7055B8AB094F4AC3432B809088C212 /* GULReachabilityChecker+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULReachabilityChecker+Internal.h"; path = "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"; sourceTree = "<group>"; }; - C03F0F7A6D77BD015418C68BDEB7FF06 /* FirebaseCore.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseCore.modulemap; sourceTree = "<group>"; }; - C5329F040451FE49D3DDAB4C1ED7F4B0 /* GULNetworkConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkConstants.h; path = GoogleUtilities/Network/Private/GULNetworkConstants.h; sourceTree = "<group>"; }; - C6A1D7E752C1123A7CF007A1C2DD71F2 /* FIRInstanceIDKeyPairUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPairUtilities.h; path = Firebase/InstanceID/FIRInstanceIDKeyPairUtilities.h; sourceTree = "<group>"; }; - C9BDDCC43E635D0901D6438F620AB5C3 /* FIRInstanceIDTokenManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenManager.h; path = Firebase/InstanceID/FIRInstanceIDTokenManager.h; sourceTree = "<group>"; }; - CC93151666D93519C2AC104874AD71FC /* FIRInstanceIDAPNSInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAPNSInfo.m; path = Firebase/InstanceID/FIRInstanceIDAPNSInfo.m; sourceTree = "<group>"; }; - CCEC7230B0E3D9061BB1D1B1D8369B51 /* FIRAnalyticsConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAnalyticsConfiguration.m; path = Firebase/Core/FIRAnalyticsConfiguration.m; sourceTree = "<group>"; }; - CDDFD3D1A057D712D16DB6E8B4D63C68 /* FIRBundleUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRBundleUtil.m; path = Firebase/Core/FIRBundleUtil.m; sourceTree = "<group>"; }; - CE1A828E33A41A2284217BC01E0085B8 /* FIRInstanceIDCheckinPreferences+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceIDCheckinPreferences+Internal.h"; path = "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h"; sourceTree = "<group>"; }; - CE46A1737F9960DE2A5BEABE471AAAB7 /* NSError+FIRInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+FIRInstanceID.h"; path = "Firebase/InstanceID/NSError+FIRInstanceID.h"; sourceTree = "<group>"; }; + B5047997309AE128643CDACF85D5C260 /* GTLRUploadParameters.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRUploadParameters.h; path = Source/Objects/GTLRUploadParameters.h; sourceTree = "<group>"; }; + B5F6A2DBC7DB5FAD70AD67EE2F401420 /* FIRInstanceIDURLQueryItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDURLQueryItem.h; path = Firebase/InstanceID/FIRInstanceIDURLQueryItem.h; sourceTree = "<group>"; }; + B6FBF86619741BAE53E3D255FBF0ABB1 /* FIRAppAssociationRegistration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAppAssociationRegistration.m; path = Firebase/Core/FIRAppAssociationRegistration.m; sourceTree = "<group>"; }; + B999E5AD02D47AC06D46363EEC291140 /* GTLRUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRUtilities.h; path = Source/Utilities/GTLRUtilities.h; sourceTree = "<group>"; }; + B9EDA8960D68B07F4E048AFDDA68BCA0 /* GTLRService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRService.m; path = Source/Objects/GTLRService.m; sourceTree = "<group>"; }; + BA38906194CC3F29E0F1F97D1A889161 /* GTLRSheetsObjects.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRSheetsObjects.h; path = Source/GeneratedServices/Sheets/GTLRSheetsObjects.h; sourceTree = "<group>"; }; + BDCD3812853BA5FE12388D33066B3CCA /* GTMSessionFetcher-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GTMSessionFetcher-Info.plist"; sourceTree = "<group>"; }; + BF158F186A2E83AA478BC8C6ECB96907 /* GoogleUtilities-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GoogleUtilities-dummy.m"; sourceTree = "<group>"; }; + C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + C2BF9601E45E61A49D75F7726674EC21 /* GoogleUtilities.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GoogleUtilities.modulemap; sourceTree = "<group>"; }; + C509EB270A3FE03A71169745013D5768 /* FIRInstanceIDCheckinService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinService.h; path = Firebase/InstanceID/FIRInstanceIDCheckinService.h; sourceTree = "<group>"; }; + C5F3BE46AB8368379E1DDE8F51A4978E /* FirebaseCore.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseCore.modulemap; sourceTree = "<group>"; }; + C635ED3519BFC49EB2C63A4252200FF2 /* GTLRSheetsQuery.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRSheetsQuery.h; path = Source/GeneratedServices/Sheets/GTLRSheetsQuery.h; sourceTree = "<group>"; }; + C6652582DEF2924B8E1639B2B90F9553 /* nanopb-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "nanopb-Info.plist"; sourceTree = "<group>"; }; + C6BA66AB7BE9C1B35D1FEEA4378F5D1C /* GoogleAPIClientForREST.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleAPIClientForREST.xcconfig; sourceTree = "<group>"; }; + C7227F8FF64B2A4BD2394FA8B9EB659B /* FirebaseCore-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "FirebaseCore-Info.plist"; sourceTree = "<group>"; }; + C99E6C3748A149B14ABA4CF063DD4F52 /* FIRBundleUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRBundleUtil.m; path = Firebase/Core/FIRBundleUtil.m; sourceTree = "<group>"; }; + C9B714FB115F77DF3A9116B5D1D89C31 /* GTLRRuntimeCommon.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRRuntimeCommon.m; path = Source/Objects/GTLRRuntimeCommon.m; sourceTree = "<group>"; }; + C9D528AA2118222E51B39583783D3824 /* Firebase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Firebase.h; path = CoreOnly/Sources/Firebase.h; sourceTree = "<group>"; }; + CC33E734D1611BA6A191D8246CEFF204 /* pb_decode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_decode.c; sourceTree = "<group>"; }; + CD7867B1787870FFF4445F7C463304EC /* GULLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLogger.h; path = GoogleUtilities/Logger/Private/GULLogger.h; sourceTree = "<group>"; }; + CDE4C3785A6E9E16C5DD045A8C3B62CA /* GTMSessionFetcher.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GTMSessionFetcher.modulemap; sourceTree = "<group>"; }; + CDECB42CD69FE8C441A88EB09E5E41B1 /* FIRInstanceID+Testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstanceID+Testing.h"; path = "Firebase/InstanceID/FIRInstanceID+Testing.h"; sourceTree = "<group>"; }; + CF8C86C20862941343D914D49B5DE74C /* GTMSessionFetcherService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTMSessionFetcherService.h; path = Source/GTMSessionFetcherService.h; sourceTree = "<group>"; }; + CFF309BE82E37F7CBFD6B6D48621D286 /* FIRInstanceIDTokenFetchOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenFetchOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h; sourceTree = "<group>"; }; + D0990D319BA2234548E2873FFC0A8BEE /* FirebaseInstanceID.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseInstanceID.modulemap; sourceTree = "<group>"; }; D2C38B5767FC35488427CDB72E1252BE /* Pods-TeachersAssistant-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TeachersAssistant-acknowledgements.plist"; sourceTree = "<group>"; }; - D314385673422BB83DB1058A7355DDF3 /* FIRInstanceIDAPNSInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDAPNSInfo.h; path = Firebase/InstanceID/FIRInstanceIDAPNSInfo.h; sourceTree = "<group>"; }; - D4FE8EA3EF498147F2DEA6E1B70F70E9 /* FIRInstanceIDKeychain.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeychain.h; path = Firebase/InstanceID/FIRInstanceIDKeychain.h; sourceTree = "<group>"; }; - D91D8C09AC5892352583E99233299DC4 /* FIRLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLogger.h; path = Firebase/Core/Private/FIRLogger.h; sourceTree = "<group>"; }; - DD326385780370B960613290BF8826DE /* nanopb-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "nanopb-dummy.m"; sourceTree = "<group>"; }; - DEA3051E00CD0FC852F9913652FB7F93 /* FIRInstanceID+Private.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FIRInstanceID+Private.m"; path = "Firebase/InstanceID/FIRInstanceID+Private.m"; sourceTree = "<group>"; }; - DEA669FCEC4147F00A475E2483BF28F6 /* FIRInstanceIDKeychain.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDKeychain.m; path = Firebase/InstanceID/FIRInstanceIDKeychain.m; sourceTree = "<group>"; }; - DF3447D3504A8DEEBC66BBF47DA3AED9 /* GULNSData+zlib.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULNSData+zlib.h"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.h"; sourceTree = "<group>"; }; - DF49DEEE320B4A7DE9468F0EC4780D19 /* FIRInstanceIDConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDConstants.h; path = Firebase/InstanceID/FIRInstanceIDConstants.h; sourceTree = "<group>"; }; - E097CA06B5E191A2ED986E28725A4B35 /* GULUserDefaults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULUserDefaults.h; path = GoogleUtilities/UserDefaults/Private/GULUserDefaults.h; sourceTree = "<group>"; }; - E1211DD77B6981F4FE4EC8D8CA0DD26C /* FIRIMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRIMessageCode.h; path = Firebase/InstanceID/FIRIMessageCode.h; sourceTree = "<group>"; }; - E4314890709AC882167685A1D464BD4A /* nanopb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = nanopb.xcconfig; sourceTree = "<group>"; }; - E5F1262F3DD318730F73C2EE5DE40EF8 /* FIRErrorCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrorCode.h; path = Firebase/Core/Private/FIRErrorCode.h; sourceTree = "<group>"; }; - E66B7745DFD45E0844D344CE0A1B0425 /* FIRInstanceIDStringEncoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDStringEncoding.h; path = Firebase/InstanceID/FIRInstanceIDStringEncoding.h; sourceTree = "<group>"; }; - E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - EA887FCB820DCD0CB25B1DBEAC519286 /* FIRVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRVersion.h; path = Firebase/Core/Private/FIRVersion.h; sourceTree = "<group>"; }; - EA88B41DC30B62C05F0F2C3DBDDB74BE /* FirebaseInstanceID-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseInstanceID-dummy.m"; sourceTree = "<group>"; }; - EB1180F966E47C1728EA6314393950BE /* FIRErrors.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrors.h; path = Firebase/Core/Private/FIRErrors.h; sourceTree = "<group>"; }; - EB68E9D6DF3F7CBD8B13F677C173E8E6 /* FIRInstanceIDCheckinPreferences.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinPreferences.m; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences.m; sourceTree = "<group>"; }; - EF86BBD38F49FEFDB78AB8A8F0A5823A /* FIRInstanceIDAuthService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDAuthService.m; path = Firebase/InstanceID/FIRInstanceIDAuthService.m; sourceTree = "<group>"; }; - F176235ACC6D4DBD16297508FF6F90C4 /* FirebaseInstanceID.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseInstanceID.modulemap; sourceTree = "<group>"; }; + D3389939168D7E16A07E06299C2D2CC6 /* FIRInstanceIDStringEncoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDStringEncoding.m; path = Firebase/InstanceID/FIRInstanceIDStringEncoding.m; sourceTree = "<group>"; }; + D9107B74D49E38BCF7B3F2ED77E71F21 /* FIRInstanceIDCheckinPreferences_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinPreferences_Private.h; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h; sourceTree = "<group>"; }; + D96D27B23ED456F3F91185B0591AA335 /* FIRInstanceIDTokenInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenInfo.h; path = Firebase/InstanceID/FIRInstanceIDTokenInfo.h; sourceTree = "<group>"; }; + D9A4276C20DF4C737ECB8CD0541D1A67 /* GTLRObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRObject.h; path = Source/Objects/GTLRObject.h; sourceTree = "<group>"; }; + D9BB6D0D6107EB6AA9292CBA994F58BD /* FIRInstanceIDTokenDeleteOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDTokenDeleteOperation.h; path = Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h; sourceTree = "<group>"; }; + DA86BE4F12D473D435458BB4FB158BCD /* FIROptions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIROptions.m; path = Firebase/Core/FIROptions.m; sourceTree = "<group>"; }; + DB136240D29989E1D1B5EDBB26789605 /* GoogleUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleUtilities.framework; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DB676188CF095CD4CF367F5EBC478EA7 /* FIRInstanceIDCheckinPreferences+Internal.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FIRInstanceIDCheckinPreferences+Internal.m"; path = "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.m"; sourceTree = "<group>"; }; + DCEE515F01DF166DBC634C843933F6B9 /* GTLRURITemplate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRURITemplate.h; path = Source/Utilities/GTLRURITemplate.h; sourceTree = "<group>"; }; + DD8C5B5B378C757D7F3D05B7BBF50F99 /* GULNetwork.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetwork.m; path = GoogleUtilities/Network/GULNetwork.m; sourceTree = "<group>"; }; + DEFA1AB5522EACA4F67A002CF883955E /* GTLRObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRObject.m; path = Source/Objects/GTLRObject.m; sourceTree = "<group>"; }; + DF5BFCC018CAF9606683F89394C3B25B /* FIRInstanceIDCheckinStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDCheckinStore.m; path = Firebase/InstanceID/FIRInstanceIDCheckinStore.m; sourceTree = "<group>"; }; + DFE9A713C35D87D3276D66BDED99B818 /* FIRInstanceIDUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDUtilities.m; path = Firebase/InstanceID/FIRInstanceIDUtilities.m; sourceTree = "<group>"; }; + E141DD28AFD58761041D850CDAE0C508 /* GULSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULSwizzler.m; path = GoogleUtilities/MethodSwizzler/GULSwizzler.m; sourceTree = "<group>"; }; + E150DF4221857596EE237D4604C64D39 /* FIRBundleUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRBundleUtil.h; path = Firebase/Core/Private/FIRBundleUtil.h; sourceTree = "<group>"; }; + E296AF2AA89291E5567866849C26499D /* GTLRDuration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRDuration.m; path = Source/Objects/GTLRDuration.m; sourceTree = "<group>"; }; + E62986E64C2AD9319C9BE39EFAD551E2 /* FIRInstanceID+Private.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FIRInstanceID+Private.m"; path = "Firebase/InstanceID/FIRInstanceID+Private.m"; sourceTree = "<group>"; }; + E63ABD34201B77F662B8D9B9DA44CDC6 /* pb_common.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_common.h; sourceTree = "<group>"; }; + E83DAD359A051E38E0DCAAC7824D73BE /* FIRInstanceIDVersionUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDVersionUtilities.m; path = Firebase/InstanceID/FIRInstanceIDVersionUtilities.m; sourceTree = "<group>"; }; + E8ABC734EC5D76410142B606FB284CB9 /* FirebaseAnalytics.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseAnalytics.xcconfig; sourceTree = "<group>"; }; + E9E92D24EC1A5967CA10893102360920 /* FIRInstanceIDKeyPair.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDKeyPair.h; path = Firebase/InstanceID/FIRInstanceIDKeyPair.h; sourceTree = "<group>"; }; + ED28B4447BE06C582F74CF53046DEFA2 /* GTLRBatchResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRBatchResult.m; path = Source/Objects/GTLRBatchResult.m; sourceTree = "<group>"; }; + ED74DCD72556A6C8618B511D2910AB18 /* FIRAnalyticsConfiguration+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRAnalyticsConfiguration+Internal.h"; path = "Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h"; sourceTree = "<group>"; }; + EEF2A1FD563D62873F1FE536E3F72F91 /* GTLRSheetsService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRSheetsService.h; path = Source/GeneratedServices/Sheets/GTLRSheetsService.h; sourceTree = "<group>"; }; + F000C09E14723D2EBBD391E19D9D5FBF /* GTMSessionFetcher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GTMSessionFetcher-umbrella.h"; sourceTree = "<group>"; }; + F047222C8056B6A3AD7E289C9EE21D28 /* FIRAnalyticsConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAnalyticsConfiguration.h; path = Firebase/Core/Public/FIRAnalyticsConfiguration.h; sourceTree = "<group>"; }; + F0D1F45EB0BE7940F2B0A3B152F0BAF0 /* GULNSData+zlib.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULNSData+zlib.h"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.h"; sourceTree = "<group>"; }; + F171712428FC85037E9F00703B7F2E22 /* GULAppDelegateSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler.h; path = GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h; sourceTree = "<group>"; }; + F22DC88B182B57DE12E328AAEF3F023C /* GTLRDateTime.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTLRDateTime.m; path = Source/Objects/GTLRDateTime.m; sourceTree = "<group>"; }; + F38AE2C8EE17A7657996595BFC9ABE11 /* FIRInstanceIDTokenManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstanceIDTokenManager.m; path = Firebase/InstanceID/FIRInstanceIDTokenManager.m; sourceTree = "<group>"; }; F529CA2B381C222BE9ECC27FA5F94111 /* Pods-TeachersAssistant-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TeachersAssistant-frameworks.sh"; sourceTree = "<group>"; }; - F5B51CAE6AAA7A3DEBCB59D9B03C90BE /* FIRInstanceID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceID.h; path = Firebase/InstanceID/Public/FIRInstanceID.h; sourceTree = "<group>"; }; - F5C80B622EEB6267EBB46092F5459CA2 /* GULReachabilityChecker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityChecker.h; path = GoogleUtilities/Reachability/Private/GULReachabilityChecker.h; sourceTree = "<group>"; }; - F63E247523E2811DA8F4C96C9D832E48 /* nanopb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = nanopb.framework; path = nanopb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F6AA10866898B71F6A21627C4E1CD14D /* FIRInstanceIDCheckinPreferences.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstanceIDCheckinPreferences.h; path = Firebase/InstanceID/FIRInstanceIDCheckinPreferences.h; sourceTree = "<group>"; }; - FBA740C4511FAFFE64FCADCFA8D55BA5 /* FirebaseCoreDiagnostics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseCoreDiagnostics.framework; path = Frameworks/FirebaseCoreDiagnostics.framework; sourceTree = "<group>"; }; - FCD467F2BC7ACAB860E989845586EE9A /* FIRComponentContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentContainer.m; path = Firebase/Core/FIRComponentContainer.m; sourceTree = "<group>"; }; + F5CF16EA7D24BA0445B470F3104A38C2 /* GULUserDefaults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULUserDefaults.h; path = GoogleUtilities/UserDefaults/Private/GULUserDefaults.h; sourceTree = "<group>"; }; + F6D4CCD7AF5F1B7C6D6D88509BAB7785 /* GTLRErrorObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GTLRErrorObject.h; path = Source/Objects/GTLRErrorObject.h; sourceTree = "<group>"; }; + FA3D37CB0B564BC0A5895A015279D8F4 /* GTMSessionFetcher-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GTMSessionFetcher-prefix.pch"; sourceTree = "<group>"; }; + FB0DA168C2F41D42C624551BC2C23F9D /* FIRErrors.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRErrors.m; path = Firebase/Core/FIRErrors.m; sourceTree = "<group>"; }; + FB866184F9DB8C552543637E0980B4FA /* NSError+FIRInstanceID.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+FIRInstanceID.m"; path = "Firebase/InstanceID/NSError+FIRInstanceID.m"; sourceTree = "<group>"; }; + FBA87A61F13A66578C76CA164240E08E /* GTMSessionFetcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GTMSessionFetcher.framework; path = GTMSessionFetcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FCC79991521336223B763662128BB7A3 /* GTMGatherInputStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GTMGatherInputStream.m; path = Source/GTMGatherInputStream.m; sourceTree = "<group>"; }; FE024B7689A72CA8CC54C9B407F9507C /* Pods-TeachersAssistantTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TeachersAssistantTests.release.xcconfig"; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -546,6 +690,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 285219F57C35AF74521FE10278556E39 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50D319FF764331FCABB481C1A7D835E5 /* Foundation.framework in Frameworks */, + 05927EA99CFB71B69E8B3851D143526B /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2AB82CCC1FE772DB4095EA633511AF3C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -564,19 +717,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BD6C01A9D3F5BF4D6F5F4588AD3A7E32 /* Frameworks */ = { + 5D2361E98237FB7C772C750B91DB5F05 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5803D830BEAB4B1992945388A716537D /* Foundation.framework in Frameworks */, + 9043BA2AD19DEE4622D0572578355D84 /* Foundation.framework in Frameworks */, + F03936351B8D3C6B5E7D5E58703FF5BE /* GTMSessionFetcher.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - BEFF10831A398C90B88A222D755D5724 /* Frameworks */ = { + A38A6054F26C426AE2C0C0EDAD0008FC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6D2D67FFD4FD3CA65BF5C77DA6EBA08B /* Foundation.framework in Frameworks */, + 2C992FD410CB75581BF435EA7A66C903 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD6C01A9D3F5BF4D6F5F4588AD3A7E32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5803D830BEAB4B1992945388A716537D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -591,74 +753,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0087E2B83AE3ECC3B1FFDC4DA4DDDF66 /* Network */ = { + 000568507EFC75DAD4832327C87A771A /* Network */ = { isa = PBXGroup; children = ( - 6DE9D4D09D58B6CC296CC31B28C71A68 /* GULMutableDictionary.h */, - 01168DD1A418F79F4BAAA97A8902A73A /* GULMutableDictionary.m */, - 0BC59F9C9C92C79BD213359E59BC0083 /* GULNetwork.h */, - 95A030E997930B42AFCBB55C290C6DC7 /* GULNetwork.m */, - C5329F040451FE49D3DDAB4C1ED7F4B0 /* GULNetworkConstants.h */, - A6BA35651106407F616D7A57FE9D73D3 /* GULNetworkConstants.m */, - A0802C848647300E14D9CEAF6F586E3D /* GULNetworkLoggerProtocol.h */, - 5471452A72F4611E3232627BC06ADD9A /* GULNetworkMessageCode.h */, - 16F8B483F73D5BC309CCBD721A6B71C0 /* GULNetworkURLSession.h */, - 4E52F5018FDC1CBA40A1B2AF3701A7FA /* GULNetworkURLSession.m */, + 83537337655F0E50082CD7E2603EFCCA /* GULMutableDictionary.h */, + 1201678D8F7875046192E6EF9294A1C4 /* GULMutableDictionary.m */, + A2672AB4665EB890F1F2E052DC068B0D /* GULNetwork.h */, + DD8C5B5B378C757D7F3D05B7BBF50F99 /* GULNetwork.m */, + 2060C1F4AD9CA43E0B2EA2B28F55A284 /* GULNetworkConstants.h */, + 74F65B52CC04DEB89684D01BB66C6E41 /* GULNetworkConstants.m */, + 00061B4668B2781E6A386D5E815509C2 /* GULNetworkLoggerProtocol.h */, + 6F130B340A708E32CAEA83F0B6619B08 /* GULNetworkMessageCode.h */, + A8CEB8453EFC3EBB8CC7F816596220DD /* GULNetworkURLSession.h */, + 6924954BD2F30993FB8230C4F0B6A5F5 /* GULNetworkURLSession.m */, ); name = Network; sourceTree = "<group>"; }; - 03C5C200A0787E300053CFA8F53CA094 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 1F34D49F868138AC113B6B8380982805 /* iOS */, - ); - name = Frameworks; - sourceTree = "<group>"; - }; - 0A0D42F1BC6E98D59A8149315B27E620 /* FirebaseCore */ = { - isa = PBXGroup; - children = ( - 5C905C3155AA4CAC5E7249613453764B /* FIRAnalyticsConfiguration.h */, - CCEC7230B0E3D9061BB1D1B1D8369B51 /* FIRAnalyticsConfiguration.m */, - 9AF42147C59B306D29A7697CF513F604 /* FIRAnalyticsConfiguration+Internal.h */, - 0721C680CFD7CCC281FAAE40CC7CC8FB /* FIRApp.h */, - B3DB5DA6985094E9B258EF0B816C7BAC /* FIRApp.m */, - 0CE321B66335679D34B98DD90B325217 /* FIRAppAssociationRegistration.h */, - ABE610632F28D152955ACE0CE2F28F03 /* FIRAppAssociationRegistration.m */, - 5C91CF94C6994C814D43E3FCD79444A0 /* FIRAppInternal.h */, - 639A8B82A185BC312DC3B6CAF5A62F01 /* FIRBundleUtil.h */, - CDDFD3D1A057D712D16DB6E8B4D63C68 /* FIRBundleUtil.m */, - 09461C56109A35828704974C4C9E9614 /* FIRComponent.h */, - 053056F16263D5265F32C049C2F8E01C /* FIRComponent.m */, - 8B55F7049963BD05146CEAF24E766A28 /* FIRComponentContainer.h */, - FCD467F2BC7ACAB860E989845586EE9A /* FIRComponentContainer.m */, - 656A5D59CF895DF019000577CFBE409D /* FIRComponentContainerInternal.h */, - 6158927EA607D8FEFA5317569E679441 /* FIRComponentType.h */, - ABA62F0D7466C8A6A84592F640B7D0E1 /* FIRComponentType.m */, - 3CDFE0A10CBBC5A70389A8808D53982E /* FIRConfiguration.h */, - 7847D97A17737D2D6CB590B13CE564CC /* FIRConfiguration.m */, - 182BE2FC262ED73F2E01FFAF232D570E /* FIRDependency.h */, - 9BDD351D126C991113400163846774B8 /* FIRDependency.m */, - 9A1269954F868B70BE772E5B5D20024F /* FirebaseCore.h */, - E5F1262F3DD318730F73C2EE5DE40EF8 /* FIRErrorCode.h */, - EB1180F966E47C1728EA6314393950BE /* FIRErrors.h */, - 2A6F1873C3979691F097FF8841EBBD85 /* FIRErrors.m */, - B04DBDA8123DF97BF5CEBC0B71C46B33 /* FIRLibrary.h */, - D91D8C09AC5892352583E99233299DC4 /* FIRLogger.h */, - AE3708AB69977852B8EC58F245468A62 /* FIRLogger.m */, - 57D0D5C4A8C0DE64D2AC9920D785D32E /* FIRLoggerLevel.h */, - 9D1936355E3B3A5466062133CD40EE0E /* FIROptions.h */, - A1828B5340482EC8D3397627ACC5AB34 /* FIROptions.m */, - 66D59167D31E9AB0B577C1597DE53BDF /* FIROptionsInternal.h */, - EA887FCB820DCD0CB25B1DBEAC519286 /* FIRVersion.h */, - 2A7C56749F7DFBFB68E361532C23A68A /* FIRVersion.m */, - 7084E13BE334652B15F73DF730AD7F92 /* Support Files */, - ); - name = FirebaseCore; - path = FirebaseCore; - sourceTree = "<group>"; - }; 0FBF9B71E9B8C5E178D8C2E26CE101D7 /* Pods-TeachersAssistantTests */ = { isa = PBXGroup; children = ( @@ -675,252 +786,320 @@ path = "Target Support Files/Pods-TeachersAssistantTests"; sourceTree = "<group>"; }; - 1F34D49F868138AC113B6B8380982805 /* iOS */ = { + 13A669684406EF1322D659F985E365A0 /* Core */ = { isa = PBXGroup; children = ( - E97B19021E1E8AF3519C89CB06E46C80 /* Foundation.framework */, - 2A62F5DEA813F07D663C4B01BE3FDEA0 /* Security.framework */, - 099602E95CC02BDF479BAE577F2E1E33 /* SystemConfiguration.framework */, - ); - name = iOS; + 504D2264E8585EE42A61DCE121FA7334 /* GTMSessionFetcher.h */, + 2CB4C2370A78826FB61E74033B2F9BFF /* GTMSessionFetcher.m */, + 8A9E981E17F06280BA9E8358D8FB90FD /* GTMSessionFetcherLogging.h */, + 0CA616F95FA0F7EB03F1582C09FCD657 /* GTMSessionFetcherLogging.m */, + CF8C86C20862941343D914D49B5DE74C /* GTMSessionFetcherService.h */, + 4BBAFAC2A9A529CD28ED16BCF13AECE4 /* GTMSessionFetcherService.m */, + 4E8A41DFF31FE019B30BF0B9770BE14D /* GTMSessionUploadFetcher.h */, + AD9455B83D23BDB4915ED6A130D1C8C4 /* GTMSessionUploadFetcher.m */, + ); + name = Core; sourceTree = "<group>"; }; - 3E0CF83956D585C0C5A074BA9C974349 /* GoogleAppMeasurement */ = { + 15F0477509C7A66A4AFAE7FA7D44C0A7 /* GoogleAPIClientForREST */ = { isa = PBXGroup; children = ( - 71E5B0AE24558DE95FA06F068114FBE6 /* Frameworks */, - E465C9C9A37D3AAF50EE35B8D991B0C1 /* Support Files */, + 6B25E6E6E4201A40D6523F4B4571E521 /* Core */, + 5872FBE37612B627A52D1E22A5570CB8 /* Sheets */, + 5D3D996D16C2D106DEE193F4FD2DD770 /* Support Files */, ); - name = GoogleAppMeasurement; - path = GoogleAppMeasurement; + name = GoogleAPIClientForREST; + path = GoogleAPIClientForREST; sourceTree = "<group>"; }; - 459B3BB97550355500B234851908FA0C /* FirebaseAnalytics */ = { + 190E7D8635BB80C952FB81757F406863 /* AppDelegateSwizzler */ = { isa = PBXGroup; children = ( - FD6C725FFDC376C9FD970F4EA44D6827 /* Frameworks */, - 6D717229D140804E9546502DB1CADEB6 /* Support Files */, + F171712428FC85037E9F00703B7F2E22 /* GULAppDelegateSwizzler.h */, + 64A59B0655E40448F960D448BFE2B4D4 /* GULAppDelegateSwizzler.m */, + A3EAF3AA8E5BADD73BCE0D2B8CC69B98 /* GULAppDelegateSwizzler_Private.h */, + 33E015D9CF73817DBAE5EE02250E5DED /* GULLoggerCodes.h */, ); - name = FirebaseAnalytics; - path = FirebaseAnalytics; + name = AppDelegateSwizzler; sourceTree = "<group>"; }; - 4C2424D8DD47BC1A281492A84B89457F /* Firebase */ = { + 222412E8F246FDD561624EAC1D1D0C97 /* Support Files */ = { isa = PBXGroup; children = ( - BFF0ACA5BDF572B26E808EA28CE381E5 /* CoreOnly */, - 6430147C71570467EB03B79056CB664A /* Support Files */, + 8416808131C45D6E3E6BC32C40C76A82 /* GoogleAppMeasurement.xcconfig */, ); - name = Firebase; - path = Firebase; + name = "Support Files"; + path = "../Target Support Files/GoogleAppMeasurement"; sourceTree = "<group>"; }; - 599A585E8608FD41C441EC698FE1CBFB /* encode */ = { + 3422C3C5D7F3C159B31E27335E13173F /* Support Files */ = { isa = PBXGroup; children = ( + 4DF7D23FF6ED5F29179DAA124D6139E0 /* nanopb.modulemap */, + ADF1B1D4D72635AB6E8D3C36D8D89A6C /* nanopb.xcconfig */, + 41F647164C5CA7901E4BFE7741255A55 /* nanopb-dummy.m */, + C6652582DEF2924B8E1639B2B90F9553 /* nanopb-Info.plist */, + 2554DC9B3976244F8B3433B862DD019F /* nanopb-prefix.pch */, + 9C00A42C8667CADDFB41282D26A16227 /* nanopb-umbrella.h */, ); - name = encode; + name = "Support Files"; + path = "../Target Support Files/nanopb"; sourceTree = "<group>"; }; - 5ADCBF928E9F66706F8962E2B0F531E0 /* Reachability */ = { + 3F6820844C317A9C2761C9E80E7D3293 /* Logger */ = { isa = PBXGroup; children = ( - F5C80B622EEB6267EBB46092F5459CA2 /* GULReachabilityChecker.h */, - 9EFC3E9C2F0E1ABA696F73E63B829CE6 /* GULReachabilityChecker.m */, - BC7055B8AB094F4AC3432B809088C212 /* GULReachabilityChecker+Internal.h */, - 06191F1C90B569E50C136B8829E2546A /* GULReachabilityMessageCode.h */, + CD7867B1787870FFF4445F7C463304EC /* GULLogger.h */, + 21E1BF177C744299F7CB68726168AE6D /* GULLogger.m */, + 58AC66E22BF3126CF2AB5A768FAABD0F /* GULLoggerLevel.h */, ); - name = Reachability; + name = Logger; sourceTree = "<group>"; }; - 5E1C348852F8ADA3D137CF5282C8EDF6 /* UserDefaults */ = { + 46D66038EF3F974B1F79DCC4014906E2 /* UserDefaults */ = { isa = PBXGroup; children = ( - E097CA06B5E191A2ED986E28725A4B35 /* GULUserDefaults.h */, - 975AEB856E53CB32B52EE02D1BC7D00C /* GULUserDefaults.m */, + F5CF16EA7D24BA0445B470F3104A38C2 /* GULUserDefaults.h */, + 0AEFDB9DD19C098BAA39381962D72398 /* GULUserDefaults.m */, ); name = UserDefaults; sourceTree = "<group>"; }; - 6430147C71570467EB03B79056CB664A /* Support Files */ = { + 48ADD341D1F72077B9B0D59546FC49FE /* MethodSwizzler */ = { isa = PBXGroup; children = ( - B90EE6D4D7C9ADED356D925B56CACE30 /* Firebase.xcconfig */, + 2B6E82FA3B095C371861788955184AAA /* GULOriginalIMPConvenienceMacros.h */, + 0B5032812C848FA7747EE94B157813B2 /* GULSwizzler.h */, + E141DD28AFD58761041D850CDAE0C508 /* GULSwizzler.m */, ); - name = "Support Files"; - path = "../Target Support Files/Firebase"; + name = MethodSwizzler; sourceTree = "<group>"; }; - 6652A34FAC728CF15C76A2E9F4C42884 /* Environment */ = { + 50EE0F54CBEA07DF918CEAA8C4E49506 /* Support Files */ = { isa = PBXGroup; children = ( - 923510B47157D70AE61469BD0DB53003 /* GULAppEnvironmentUtil.h */, - 2F822B32EED40D27314DFE21C5EDDA6A /* GULAppEnvironmentUtil.m */, + CDE4C3785A6E9E16C5DD045A8C3B62CA /* GTMSessionFetcher.modulemap */, + 0A22E7D0390892ECB350FFF998A271D0 /* GTMSessionFetcher.xcconfig */, + 09CF74BB4F08D9BA981B0FCB07128326 /* GTMSessionFetcher-dummy.m */, + BDCD3812853BA5FE12388D33066B3CCA /* GTMSessionFetcher-Info.plist */, + FA3D37CB0B564BC0A5895A015279D8F4 /* GTMSessionFetcher-prefix.pch */, + F000C09E14723D2EBBD391E19D9D5FBF /* GTMSessionFetcher-umbrella.h */, ); - name = Environment; + name = "Support Files"; + path = "../Target Support Files/GTMSessionFetcher"; sourceTree = "<group>"; }; - 67071B03E68636F690B3FA228B2E208D /* Support Files */ = { + 5872FBE37612B627A52D1E22A5570CB8 /* Sheets */ = { isa = PBXGroup; children = ( - 7DB356CAA2B666246D87D8FC92ABD8F2 /* nanopb.modulemap */, - E4314890709AC882167685A1D464BD4A /* nanopb.xcconfig */, - DD326385780370B960613290BF8826DE /* nanopb-dummy.m */, - 6519211EB05611CA463B38D0D316C570 /* nanopb-Info.plist */, - 95A4EBC5E3FF3C286D3DCF3C04A8320A /* nanopb-prefix.pch */, - 84E1E0BB483A5ED582BCF730EFC79223 /* nanopb-umbrella.h */, + 0E9773C37D034C604399ABF8364696FC /* GTLRSheets.h */, + BA38906194CC3F29E0F1F97D1A889161 /* GTLRSheetsObjects.h */, + B17C169B2ADA959DE38DAE86D07B7379 /* GTLRSheetsObjects.m */, + C635ED3519BFC49EB2C63A4252200FF2 /* GTLRSheetsQuery.h */, + 20CB19971EEB7403662B8D6D969AAA1B /* GTLRSheetsQuery.m */, + EEF2A1FD563D62873F1FE536E3F72F91 /* GTLRSheetsService.h */, + 1DEDD6F11A6592A96F72E6E90EC1FD73 /* GTLRSheetsService.m */, + ); + name = Sheets; + sourceTree = "<group>"; + }; + 5D3D996D16C2D106DEE193F4FD2DD770 /* Support Files */ = { + isa = PBXGroup; + children = ( + B240966BF419D8B916096F61465022D1 /* GoogleAPIClientForREST.modulemap */, + C6BA66AB7BE9C1B35D1FEEA4378F5D1C /* GoogleAPIClientForREST.xcconfig */, + 359D1387EF23CF339959AB5D298D182D /* GoogleAPIClientForREST-dummy.m */, + 7ECD67C851A9B1AAF8CB0A1D65A67194 /* GoogleAPIClientForREST-Info.plist */, + 385F92547477A014104A69ADC304BCAD /* GoogleAPIClientForREST-prefix.pch */, + 8AB3ECC077B85578CA86B1AB55A7B12D /* GoogleAPIClientForREST-umbrella.h */, ); name = "Support Files"; - path = "../Target Support Files/nanopb"; + path = "../Target Support Files/GoogleAPIClientForREST"; sourceTree = "<group>"; }; - 6D717229D140804E9546502DB1CADEB6 /* Support Files */ = { + 5DFB3D0FCDA8985A1D96E5FDC266EAE2 /* iOS */ = { isa = PBXGroup; children = ( - 15E28D734540855F231814108CF62173 /* FirebaseAnalytics.xcconfig */, + C1DBB860A02D6C05F0481A5858569152 /* Foundation.framework */, + 4D669732E0BA0105016B0BFAF2C9EEE1 /* Security.framework */, + 0F0A5A680330D1BBE6109D7E21EEBA20 /* SystemConfiguration.framework */, ); - name = "Support Files"; - path = "../Target Support Files/FirebaseAnalytics"; + name = iOS; sourceTree = "<group>"; }; - 7084E13BE334652B15F73DF730AD7F92 /* Support Files */ = { + 61034BDB34982019CEDBE9751923C987 /* GoogleAppMeasurement */ = { isa = PBXGroup; children = ( - C03F0F7A6D77BD015418C68BDEB7FF06 /* FirebaseCore.modulemap */, - AFF19F90C766B8C2B9A22DE86B1BFC0F /* FirebaseCore.xcconfig */, - A14A4950DB0EDF3B8A678040F6D096DB /* FirebaseCore-dummy.m */, - 24CF8D872F7CF1995FC4C686A806A211 /* FirebaseCore-Info.plist */, - 01B1A17AD079D9B1CC5F2566896555AE /* FirebaseCore-umbrella.h */, + FB89EE033F8E78976FB7923B48CEB037 /* Frameworks */, + 222412E8F246FDD561624EAC1D1D0C97 /* Support Files */, ); - name = "Support Files"; - path = "../Target Support Files/FirebaseCore"; + name = GoogleAppMeasurement; + path = GoogleAppMeasurement; sourceTree = "<group>"; }; - 71E5B0AE24558DE95FA06F068114FBE6 /* Frameworks */ = { + 6B25E6E6E4201A40D6523F4B4571E521 /* Core */ = { isa = PBXGroup; children = ( - B7B6121D361EE7F4909DE2D1943F322B /* GoogleAppMeasurement.framework */, + 632CEDCB8B1B6CADF8FA17EE175367B8 /* GTLRBase64.h */, + A12B17E00FB8FCB54E94D10975B5302C /* GTLRBase64.m */, + 74EF9C835171D35F298E15ACA4B38506 /* GTLRBatchQuery.h */, + 73DEB6CDF7421F78C01F7474A67404F3 /* GTLRBatchQuery.m */, + ADB38B25ABA9170879CD55B53412DFE6 /* GTLRBatchResult.h */, + ED28B4447BE06C582F74CF53046DEFA2 /* GTLRBatchResult.m */, + 0810102125B8C9E4FBBF7EF7D58409CC /* GTLRDateTime.h */, + F22DC88B182B57DE12E328AAEF3F023C /* GTLRDateTime.m */, + 56CE98F133AB2BA1382EB2FAB97B0AF8 /* GTLRDefines.h */, + 2CA56DE4B76F14F33BDA8853272D8366 /* GTLRDuration.h */, + E296AF2AA89291E5567866849C26499D /* GTLRDuration.m */, + F6D4CCD7AF5F1B7C6D6D88509BAB7785 /* GTLRErrorObject.h */, + 4673860D113355AAD052F21D1D4399F7 /* GTLRErrorObject.m */, + 217305076F6C446D80ECEE650D6FF336 /* GTLRFramework.h */, + 9F12DC4DF7E7F8A371738879B374FFF5 /* GTLRFramework.m */, + D9A4276C20DF4C737ECB8CD0541D1A67 /* GTLRObject.h */, + DEFA1AB5522EACA4F67A002CF883955E /* GTLRObject.m */, + 91FD3DD45495B59CF1AD18C2F1C5664A /* GTLRQuery.h */, + 54F922D095E5B9A857BABDE9A29D2EA7 /* GTLRQuery.m */, + 7AF84FD306398BAAB32D5EB452C1E569 /* GTLRRuntimeCommon.h */, + C9B714FB115F77DF3A9116B5D1D89C31 /* GTLRRuntimeCommon.m */, + 2F3C1179BBE0B2F619AB8D842242DF54 /* GTLRService.h */, + B9EDA8960D68B07F4E048AFDDA68BCA0 /* GTLRService.m */, + B5047997309AE128643CDACF85D5C260 /* GTLRUploadParameters.h */, + 6004EF0F526D87CB1DB9CE9FE43FBE40 /* GTLRUploadParameters.m */, + DCEE515F01DF166DBC634C843933F6B9 /* GTLRURITemplate.h */, + 4B6FFA90600425E042D0DB9FE2BCF44F /* GTLRURITemplate.m */, + B999E5AD02D47AC06D46363EEC291140 /* GTLRUtilities.h */, + 5E92A8BA31159E11D52BAF17FD3D11F2 /* GTLRUtilities.m */, + ); + name = Core; + sourceTree = "<group>"; + }; + 6B9A9C6008E62201B938BD6DF406BD76 /* Reachability */ = { + isa = PBXGroup; + children = ( + 7CCAEF0B1870F052EDE7D124EE35E5F2 /* GULReachabilityChecker.h */, + 5422968193C250477308AB011920863A /* GULReachabilityChecker.m */, + 9E00DA041CCC31B6AEC875C52444A935 /* GULReachabilityChecker+Internal.h */, + 7DA698D652FCFAC16C9EEFCA83C85E72 /* GULReachabilityMessageCode.h */, ); - name = Frameworks; + name = Reachability; sourceTree = "<group>"; }; - 81E55F5D3C08B8318CC6A7C7D7B539F7 /* nanopb */ = { + 6D6F748954782717B496DFBC7AD91379 /* nanopb */ = { isa = PBXGroup; children = ( - 933E7CB41F265BF8575D8E6571D4F745 /* pb.h */, - AE892269DC64E59580E85799336B0E3F /* pb_common.c */, - 10C2190ED503628386EB3FA0ACBBBC13 /* pb_common.h */, - 8D8E654970A93A22D15B4DB3F736820B /* pb_decode.c */, - 63F5C9FB083CECFE9AD2689CAFED23FE /* pb_decode.h */, - A183E0073F3551A0DFFBAD1EE4650667 /* pb_encode.c */, - B61D10491AFFFCEF1A08FB83E4F44A0A /* pb_encode.h */, - D0F07F57E977CDCCD45C86C6FBB4DB8D /* decode */, - 599A585E8608FD41C441EC698FE1CBFB /* encode */, - 67071B03E68636F690B3FA228B2E208D /* Support Files */, + 26B79195C4C8B553F57B3D3535D34328 /* pb.h */, + 3CAC70B7C1A8906857FA3FC99C1BD4EC /* pb_common.c */, + E63ABD34201B77F662B8D9B9DA44CDC6 /* pb_common.h */, + CC33E734D1611BA6A191D8246CEFF204 /* pb_decode.c */, + 734E5DCCCE857FFA34745EB5C2D6C5EA /* pb_decode.h */, + 6C217B0FC3899164615FD61D788E7FBA /* pb_encode.c */, + 2F783D0C9291771FE7CD74A47090BDE4 /* pb_encode.h */, + BB34A8F19260533021E56F35106A3D9E /* decode */, + ED948ECEDEC70E540465331CF6AD793B /* encode */, + 3422C3C5D7F3C159B31E27335E13173F /* Support Files */, ); name = nanopb; path = nanopb; sourceTree = "<group>"; }; - 8C70AF138CF55CC76B32F93E3E170B26 /* FirebaseInstanceID */ = { + 853EC0BD12F56AA7C7F2C10C782D1A2A /* Support Files */ = { isa = PBXGroup; children = ( - 3922B447A4A2A874121C267E40012CD2 /* FirebaseInstanceID.h */, - E1211DD77B6981F4FE4EC8D8CA0DD26C /* FIRIMessageCode.h */, - F5B51CAE6AAA7A3DEBCB59D9B03C90BE /* FIRInstanceID.h */, - 093DFF10EA7BD9115772D1D2AA477795 /* FIRInstanceID.m */, - 55866B95A313509A4206813BC4B48CD6 /* FIRInstanceID+Private.h */, - DEA3051E00CD0FC852F9913652FB7F93 /* FIRInstanceID+Private.m */, - 3D4B5C9B43DB2526EF6BF314E82C38F1 /* FIRInstanceID+Testing.h */, - D314385673422BB83DB1058A7355DDF3 /* FIRInstanceIDAPNSInfo.h */, - CC93151666D93519C2AC104874AD71FC /* FIRInstanceIDAPNSInfo.m */, - 6DED144D39D4BC1B72D7492161128B2E /* FIRInstanceIDAuthKeyChain.h */, - 16ABBA3AED50ADB109B70827858F46FB /* FIRInstanceIDAuthKeyChain.m */, - 2AF1A6BD7C03AE56C8644D4BF79E2E78 /* FIRInstanceIDAuthService.h */, - EF86BBD38F49FEFDB78AB8A8F0A5823A /* FIRInstanceIDAuthService.m */, - A19E92882D90BF25AF701FA415D5168C /* FIRInstanceIDBackupExcludedPlist.h */, - 36376442373649570509651445F690F2 /* FIRInstanceIDBackupExcludedPlist.m */, - F6AA10866898B71F6A21627C4E1CD14D /* FIRInstanceIDCheckinPreferences.h */, - EB68E9D6DF3F7CBD8B13F677C173E8E6 /* FIRInstanceIDCheckinPreferences.m */, - CE1A828E33A41A2284217BC01E0085B8 /* FIRInstanceIDCheckinPreferences+Internal.h */, - 68F3D9F0F4CB406856DE551E00F4BA94 /* FIRInstanceIDCheckinPreferences+Internal.m */, - 74804560F99741B16093413E3ED271A1 /* FIRInstanceIDCheckinPreferences_Private.h */, - 08B3E50C44804158B340813635E6BC09 /* FIRInstanceIDCheckinService.h */, - AD786D532408C86D1EB6C927B0C87A75 /* FIRInstanceIDCheckinService.m */, - 6D51E316195287C40904DFCF52B1B8B3 /* FIRInstanceIDCheckinStore.h */, - 94F3B242B6FA54A454F2784A59455D37 /* FIRInstanceIDCheckinStore.m */, - DF49DEEE320B4A7DE9468F0EC4780D19 /* FIRInstanceIDConstants.h */, - B6677173DD856010DD44F240491627F5 /* FIRInstanceIDConstants.m */, - 30CA044A7E9853B9620403801FFD468A /* FIRInstanceIDDefines.h */, - D4FE8EA3EF498147F2DEA6E1B70F70E9 /* FIRInstanceIDKeychain.h */, - DEA669FCEC4147F00A475E2483BF28F6 /* FIRInstanceIDKeychain.m */, - 3F162645BE355033F19D49D40BEA124E /* FIRInstanceIDKeyPair.h */, - 4FD50293995AD747738A754E700980D2 /* FIRInstanceIDKeyPair.m */, - 034660AA71C364B6CE19A7775ACBFF3F /* FIRInstanceIDKeyPairStore.h */, - 0197914611D5254348329F289DE67F28 /* FIRInstanceIDKeyPairStore.m */, - C6A1D7E752C1123A7CF007A1C2DD71F2 /* FIRInstanceIDKeyPairUtilities.h */, - 7397308853202DF815AD5A1CD1C07E45 /* FIRInstanceIDKeyPairUtilities.m */, - 87278A4E974DA21F7BA3EE74DE03DADE /* FIRInstanceIDLogger.h */, - 87F87803FFB82959EC1D3D60F01B0458 /* FIRInstanceIDLogger.m */, - 69D06FAE028081AF31F2CC18622AEB9B /* FIRInstanceIDStore.h */, - 600DA0E155B6B8E031A9B0DB8F58EC5F /* FIRInstanceIDStore.m */, - E66B7745DFD45E0844D344CE0A1B0425 /* FIRInstanceIDStringEncoding.h */, - 18437710751F31983A4B4343B6AE0846 /* FIRInstanceIDStringEncoding.m */, - 90D2864C001A2BBBDC9E2870EE0A692A /* FIRInstanceIDTokenDeleteOperation.h */, - 58F0868863F06C7865D23136C4091147 /* FIRInstanceIDTokenDeleteOperation.m */, - 6E439097F5D3E57E08E7B5A0CF53E344 /* FIRInstanceIDTokenFetchOperation.h */, - B754517F46C50C45E8EA76654D73524D /* FIRInstanceIDTokenFetchOperation.m */, - 4242EFC1874D23623622538D952F8B5B /* FIRInstanceIDTokenInfo.h */, - A3FE1AA32CB9692E06D6A3C4001459B8 /* FIRInstanceIDTokenInfo.m */, - C9BDDCC43E635D0901D6438F620AB5C3 /* FIRInstanceIDTokenManager.h */, - 29E9D0A0280D12D4D31B3C8C33CB540A /* FIRInstanceIDTokenManager.m */, - 9BAC6A2CCC656481599672B6E990A5BD /* FIRInstanceIDTokenOperation.h */, - B56AAB2892E7D78A490ACD5C6E90A87F /* FIRInstanceIDTokenOperation.m */, - 16D15E2B7F18A663F0CB385DB75F0DC1 /* FIRInstanceIDTokenOperation+Private.h */, - 64B7AC5ED456E5A4AB1B41E61E53FF71 /* FIRInstanceIDTokenStore.h */, - 2B6E9D37CB97B82EC30EDA29C24BFA28 /* FIRInstanceIDTokenStore.m */, - 10DE2EBFE2C271E7C4FDB62DEB56A09E /* FIRInstanceIDURLQueryItem.h */, - 6CBD9DE48945BE5D8D3F6DF83BCAEBB7 /* FIRInstanceIDURLQueryItem.m */, - 21652FC291F729CE79FCAA2D04B79CE8 /* FIRInstanceIDUtilities.h */, - 8BC45FD590053D7BF61B9B75DB594D11 /* FIRInstanceIDUtilities.m */, - 76F8720EE48E3A1D2085F0BC9F06CEE5 /* FIRInstanceIDVersionUtilities.h */, - 6A0A7EDEA8C34882ED9F21855FFD6B54 /* FIRInstanceIDVersionUtilities.m */, - CE46A1737F9960DE2A5BEABE471AAAB7 /* NSError+FIRInstanceID.h */, - B70A1D762675BEC99064CE31C0656656 /* NSError+FIRInstanceID.m */, - 9CB8BA0F94EF2C366B184E9253B26D2B /* Support Files */, + C5F3BE46AB8368379E1DDE8F51A4978E /* FirebaseCore.modulemap */, + 66B1B5E0F02389648F7D7F08EE92449E /* FirebaseCore.xcconfig */, + 312A54AECBD7450DEF1199ACE70B112A /* FirebaseCore-dummy.m */, + C7227F8FF64B2A4BD2394FA8B9EB659B /* FirebaseCore-Info.plist */, + 7110D63F53E9A413E26235ED20E973C9 /* FirebaseCore-umbrella.h */, ); - name = FirebaseInstanceID; - path = FirebaseInstanceID; + name = "Support Files"; + path = "../Target Support Files/FirebaseCore"; sourceTree = "<group>"; }; - 9AFA54316C8D6149E7425B0C64723281 /* GoogleUtilities */ = { + 85BDCBE8335926A5DB41B3C841A3C263 /* Pods */ = { isa = PBXGroup; children = ( - ECBE037778614C9F620F12F5D0FE936A /* AppDelegateSwizzler */, - 6652A34FAC728CF15C76A2E9F4C42884 /* Environment */, - E9D7766E604C6484F91D520CBEB15F72 /* Logger */, - D0B280DDCA2FDA0EC4D9D37E38C22CDF /* MethodSwizzler */, - 0087E2B83AE3ECC3B1FFDC4DA4DDDF66 /* Network */, - B27950AD5D4C082FF5E9CE6C2DFB3959 /* NSData+zlib */, - 5ADCBF928E9F66706F8962E2B0F531E0 /* Reachability */, - D7AD9E405FB6D503AA3B8482847C0B88 /* Support Files */, - 5E1C348852F8ADA3D137CF5282C8EDF6 /* UserDefaults */, + E51C279489632B4AE3A37D169F20A5FE /* Firebase */, + 8AC2F05606201D445330DBBA99B8FCE8 /* FirebaseAnalytics */, + AD1B20E72047E995DA3A5BB3EB0B4B38 /* FirebaseCore */, + D5C309574E054758426FF93D19E5CA99 /* FirebaseInstanceID */, + 15F0477509C7A66A4AFAE7FA7D44C0A7 /* GoogleAPIClientForREST */, + 61034BDB34982019CEDBE9751923C987 /* GoogleAppMeasurement */, + C120AAFD9B8F9040B8CB01A9F341ADCC /* GoogleUtilities */, + D8E1E1B643379135870A88FE866DF9BE /* GTMSessionFetcher */, + 6D6F748954782717B496DFBC7AD91379 /* nanopb */, ); - name = GoogleUtilities; - path = GoogleUtilities; + name = Pods; + sourceTree = "<group>"; + }; + 8AC2F05606201D445330DBBA99B8FCE8 /* FirebaseAnalytics */ = { + isa = PBXGroup; + children = ( + C72D17D0EE04F174CCC70EF0C4FACA9C /* Frameworks */, + 9DEB7A68F141922B221C5088B5293CEB /* Support Files */, + ); + name = FirebaseAnalytics; + path = FirebaseAnalytics; sourceTree = "<group>"; }; - 9CB8BA0F94EF2C366B184E9253B26D2B /* Support Files */ = { + 95D80BD57F09D32EBA1416C100D6161A /* Support Files */ = { isa = PBXGroup; children = ( - F176235ACC6D4DBD16297508FF6F90C4 /* FirebaseInstanceID.modulemap */, - 5C49A18BA48C07644DDE0215A595578E /* FirebaseInstanceID.xcconfig */, - EA88B41DC30B62C05F0F2C3DBDDB74BE /* FirebaseInstanceID-dummy.m */, - 2C6DFF7CA7C4CD341941DDF6A70B8A04 /* FirebaseInstanceID-Info.plist */, - 9A4F2F1F710FE4FF3A1ACDAB7711391D /* FirebaseInstanceID-umbrella.h */, + 069518B8B1116A7286048E10777CE4B1 /* Firebase.xcconfig */, ); name = "Support Files"; - path = "../Target Support Files/FirebaseInstanceID"; + path = "../Target Support Files/Firebase"; + sourceTree = "<group>"; + }; + 9DEB7A68F141922B221C5088B5293CEB /* Support Files */ = { + isa = PBXGroup; + children = ( + E8ABC734EC5D76410142B606FB284CB9 /* FirebaseAnalytics.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/FirebaseAnalytics"; + sourceTree = "<group>"; + }; + AD1B20E72047E995DA3A5BB3EB0B4B38 /* FirebaseCore */ = { + isa = PBXGroup; + children = ( + F047222C8056B6A3AD7E289C9EE21D28 /* FIRAnalyticsConfiguration.h */, + 5D74E330C10A59ECB0E3A651183B5902 /* FIRAnalyticsConfiguration.m */, + ED74DCD72556A6C8618B511D2910AB18 /* FIRAnalyticsConfiguration+Internal.h */, + 4AAFBE6129D7F7EE3751814049C55310 /* FIRApp.h */, + 271F6CEBA30B3B7191840DEDD47F81FC /* FIRApp.m */, + A62C33CAAD941D27993CFB927857A476 /* FIRAppAssociationRegistration.h */, + B6FBF86619741BAE53E3D255FBF0ABB1 /* FIRAppAssociationRegistration.m */, + 042BE6629EC9A2E9F87D17CED7AACB6A /* FIRAppInternal.h */, + E150DF4221857596EE237D4604C64D39 /* FIRBundleUtil.h */, + C99E6C3748A149B14ABA4CF063DD4F52 /* FIRBundleUtil.m */, + 499E59FADC4F9A4FD57637B40A7E0AEB /* FIRComponent.h */, + 8AC4E56443368A59F770E05990328FBB /* FIRComponent.m */, + 8AF638AC9C581DA7EAF1350F43C6E347 /* FIRComponentContainer.h */, + 4E3ADB623A71EF1040F07F4474B13C1F /* FIRComponentContainer.m */, + 72A68E1354B8F68044AB4DD21E274D55 /* FIRComponentContainerInternal.h */, + 26B7B16A2741E588921CA76370A6E0C6 /* FIRComponentType.h */, + 7CC67CCBC2D46F79CC57C3F798122C7E /* FIRComponentType.m */, + 07C940E5390B103F4CE2892BFFBC987A /* FIRConfiguration.h */, + 82BB2879569D536A6530B687161DC8B1 /* FIRConfiguration.m */, + 2702574391D7BC669EE01BBCB336A64C /* FIRDependency.h */, + 0508F9F459470F87AE900EE671A33683 /* FIRDependency.m */, + 5AA8AEFC53E239B1C44121C6399BDF14 /* FirebaseCore.h */, + 637245938FD074F3B9AE443C80D0A34E /* FIRErrorCode.h */, + 8CA5A0BB0A7F36E8F7189D91C8B29728 /* FIRErrors.h */, + FB0DA168C2F41D42C624551BC2C23F9D /* FIRErrors.m */, + 374883FE51263B2BD861B86677BF5ECA /* FIRLibrary.h */, + 6DE4621B5BF4A7F747C7A66937E79855 /* FIRLogger.h */, + 745F466B0E0CFD0057D71D440A6D7D4D /* FIRLogger.m */, + 20BB64A0D3C190E922B42A7DABDF4351 /* FIRLoggerLevel.h */, + 7C62C59F592BBF3BA1637FDA859BE9A6 /* FIROptions.h */, + DA86BE4F12D473D435458BB4FB158BCD /* FIROptions.m */, + 1A3A538DA0C18DD8A698010361D27A8A /* FIROptionsInternal.h */, + 357B0ECED3D296D4A9739C22E28FF76D /* FIRVersion.h */, + 321D5E4C31CED38A5BE1C3397595E7F5 /* FIRVersion.m */, + 853EC0BD12F56AA7C7F2C10C782D1A2A /* Support Files */, + ); + name = FirebaseCore; + path = FirebaseCore; sourceTree = "<group>"; }; AEB6E5ED4592D8BEF6808CBB5ABD44EA /* Pods-TeachersAssistant */ = { @@ -940,63 +1119,175 @@ path = "Target Support Files/Pods-TeachersAssistant"; sourceTree = "<group>"; }; - B27950AD5D4C082FF5E9CE6C2DFB3959 /* NSData+zlib */ = { + AFD5746B3322A5A7717FD58DEC1AB0C6 /* Full */ = { + isa = PBXGroup; + children = ( + 5BC9D2775C1E7A3C43A2D9FB68FD6140 /* GTMGatherInputStream.h */, + FCC79991521336223B763662128BB7A3 /* GTMGatherInputStream.m */, + 88278CB0E802158640AF8B0E24FAD79A /* GTMMIMEDocument.h */, + 4370B2DFACB00E7DEC0868A68AA87DBD /* GTMMIMEDocument.m */, + 8795560D80511CF84F3EF6325D2A8F51 /* GTMReadMonitorInputStream.h */, + 547B4BA4FC4AC94B80BCCBAA38BEEB88 /* GTMReadMonitorInputStream.m */, + ); + name = Full; + sourceTree = "<group>"; + }; + BB34A8F19260533021E56F35106A3D9E /* decode */ = { isa = PBXGroup; children = ( - DF3447D3504A8DEEBC66BBF47DA3AED9 /* GULNSData+zlib.h */, - BA3D88362D7798228C9653A26168D35E /* GULNSData+zlib.m */, ); - name = "NSData+zlib"; + name = decode; sourceTree = "<group>"; }; - BFF0ACA5BDF572B26E808EA28CE381E5 /* CoreOnly */ = { + C120AAFD9B8F9040B8CB01A9F341ADCC /* GoogleUtilities */ = { isa = PBXGroup; children = ( - 0EB8F5A1B2B1B249D6F54B422FAEF791 /* Firebase.h */, + 190E7D8635BB80C952FB81757F406863 /* AppDelegateSwizzler */, + CD1D88E16DF89CB347322C5845068DED /* Environment */, + 3F6820844C317A9C2761C9E80E7D3293 /* Logger */, + 48ADD341D1F72077B9B0D59546FC49FE /* MethodSwizzler */, + 000568507EFC75DAD4832327C87A771A /* Network */, + ED5AEF75C2F9AC611441207C0681F931 /* NSData+zlib */, + 6B9A9C6008E62201B938BD6DF406BD76 /* Reachability */, + E34D4A6C379908310BCC0023B51767E5 /* Support Files */, + 46D66038EF3F974B1F79DCC4014906E2 /* UserDefaults */, ); - name = CoreOnly; + name = GoogleUtilities; + path = GoogleUtilities; + sourceTree = "<group>"; + }; + C72D17D0EE04F174CCC70EF0C4FACA9C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7D8784AB13BF6FBDE11552EE49C12DCC /* FIRAnalyticsConnector.framework */, + 123604314885A641B32BDF8B0F9DA302 /* FirebaseAnalytics.framework */, + 83DF78C7972D2E89918296EE5DA73881 /* FirebaseCoreDiagnostics.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + C867A118C912B19773323DA8F4417A47 /* Support Files */ = { + isa = PBXGroup; + children = ( + D0990D319BA2234548E2873FFC0A8BEE /* FirebaseInstanceID.modulemap */, + 26EEA6C6A294786C4626A5D90B0CC7A8 /* FirebaseInstanceID.xcconfig */, + 2EDD142F7B8EDBDDDDB5BF548CD9DED8 /* FirebaseInstanceID-dummy.m */, + AD26A272AD866767EC8863C068DAC7A9 /* FirebaseInstanceID-Info.plist */, + 87191E9BA56B22A703AD591A7963F5E3 /* FirebaseInstanceID-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/FirebaseInstanceID"; + sourceTree = "<group>"; + }; + CD1D88E16DF89CB347322C5845068DED /* Environment */ = { + isa = PBXGroup; + children = ( + 6C7078467D0A33F3EBEFE639202A278A /* GULAppEnvironmentUtil.h */, + 294D8D5384102F5EC8F36C99BD40B553 /* GULAppEnvironmentUtil.m */, + ); + name = Environment; sourceTree = "<group>"; }; CF1408CF629C7361332E53B88F7BD30C = { isa = PBXGroup; children = ( 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - 03C5C200A0787E300053CFA8F53CA094 /* Frameworks */, - E5D6002B5E9D0AB903235E8741F4E21C /* Pods */, - E47968FDAD5BDD7F20DB6D52472B5D29 /* Products */, + DB6645AFD4BCB1190B7697DA96230830 /* Frameworks */, + 85BDCBE8335926A5DB41B3C841A3C263 /* Pods */, + F2A8EA43702E1C27A51E54A819C55FE7 /* Products */, E201F6F1A7795FB7DB590924C510BDB4 /* Targets Support Files */, ); sourceTree = "<group>"; }; - D0B280DDCA2FDA0EC4D9D37E38C22CDF /* MethodSwizzler */ = { + D5C309574E054758426FF93D19E5CA99 /* FirebaseInstanceID */ = { isa = PBXGroup; children = ( - 5B956219AB913B55F60DC376E29FADC1 /* GULOriginalIMPConvenienceMacros.h */, - 8CFE5D0397A33F3BB0D159D063A0730D /* GULSwizzler.h */, - 32CED92C605AFBFFF75E32B97F855D08 /* GULSwizzler.m */, + 15D8C40F17E82EDAAA2FD2FEE97387AD /* FirebaseInstanceID.h */, + 4094FA6772FCD62F992E63FD03A145DC /* FIRIMessageCode.h */, + 30630E98C2C2879DE2C1248CA2C52F19 /* FIRInstanceID.h */, + 49C0CD9206E359DF82CB5D71E5B5097F /* FIRInstanceID.m */, + 52C6C4BA62B48087FCEC1011C608BDBA /* FIRInstanceID+Private.h */, + E62986E64C2AD9319C9BE39EFAD551E2 /* FIRInstanceID+Private.m */, + CDECB42CD69FE8C441A88EB09E5E41B1 /* FIRInstanceID+Testing.h */, + 1331D13DD4DE386BCEDBC001F6B10B28 /* FIRInstanceIDAPNSInfo.h */, + 580320C3A739577369ED81033BFCE123 /* FIRInstanceIDAPNSInfo.m */, + 3E8692905552E367BFE89057E3905DB9 /* FIRInstanceIDAuthKeyChain.h */, + 8F06F1782AC0D2EF8E359A25024009A7 /* FIRInstanceIDAuthKeyChain.m */, + 1911D2DEEADE2AAC254508D0D8D4DD05 /* FIRInstanceIDAuthService.h */, + A36649E2942CA43CF746CB8BB3356A9D /* FIRInstanceIDAuthService.m */, + 108C5CDFDC3BEA327489B1E055ED7470 /* FIRInstanceIDBackupExcludedPlist.h */, + 516450B002D7F6CFAD2116CE501F57A3 /* FIRInstanceIDBackupExcludedPlist.m */, + 17CCF5036BBF759B3851A291325A58DD /* FIRInstanceIDCheckinPreferences.h */, + B2CE9C8D3C9509E03742DAAC7D484973 /* FIRInstanceIDCheckinPreferences.m */, + A6EFE17E8C1D2435651AA1CF3C3314CB /* FIRInstanceIDCheckinPreferences+Internal.h */, + DB676188CF095CD4CF367F5EBC478EA7 /* FIRInstanceIDCheckinPreferences+Internal.m */, + D9107B74D49E38BCF7B3F2ED77E71F21 /* FIRInstanceIDCheckinPreferences_Private.h */, + C509EB270A3FE03A71169745013D5768 /* FIRInstanceIDCheckinService.h */, + 0AA2DD0AD8A2506D0FC3BBBF05067226 /* FIRInstanceIDCheckinService.m */, + 0BBF3A739B0E8360360E868AA65D007D /* FIRInstanceIDCheckinStore.h */, + DF5BFCC018CAF9606683F89394C3B25B /* FIRInstanceIDCheckinStore.m */, + 347A040A0C9EF78AE6E2CC3305BA6394 /* FIRInstanceIDConstants.h */, + 2897AC5168624488338884B7C36B5865 /* FIRInstanceIDConstants.m */, + 6BC4FA64AE2079B4E811849A1860A4A6 /* FIRInstanceIDDefines.h */, + 7943A53065BB0DF789C3CDEEC1004CD9 /* FIRInstanceIDKeychain.h */, + 4CF4F8A5BE53C6A071CEA011E4022665 /* FIRInstanceIDKeychain.m */, + E9E92D24EC1A5967CA10893102360920 /* FIRInstanceIDKeyPair.h */, + 7FBB2D1B3717FA3F20CC83DA53908482 /* FIRInstanceIDKeyPair.m */, + 290A38F9B7BEF687F3DBB3C3255CC233 /* FIRInstanceIDKeyPairStore.h */, + 63CA82E29BA583AD0A52D7A37558E2F9 /* FIRInstanceIDKeyPairStore.m */, + 500B9CE6A2B5ECC69A261EE855D14BDC /* FIRInstanceIDKeyPairUtilities.h */, + 3895CC74E7806861C8DB5D96CD95C007 /* FIRInstanceIDKeyPairUtilities.m */, + 43D7AEB5BE9D344529D075789E88E926 /* FIRInstanceIDLogger.h */, + A737C343B6C79B154315978701F29A04 /* FIRInstanceIDLogger.m */, + 559A7AF75C7825A882D221F994EDE0FE /* FIRInstanceIDStore.h */, + 61C9D32696A6761D07B73E8E6A1B0603 /* FIRInstanceIDStore.m */, + 3AA55CFA88ACE2B99420F368974ED9E7 /* FIRInstanceIDStringEncoding.h */, + D3389939168D7E16A07E06299C2D2CC6 /* FIRInstanceIDStringEncoding.m */, + D9BB6D0D6107EB6AA9292CBA994F58BD /* FIRInstanceIDTokenDeleteOperation.h */, + 34F74B35ABB197CD426E4B528256BA88 /* FIRInstanceIDTokenDeleteOperation.m */, + CFF309BE82E37F7CBFD6B6D48621D286 /* FIRInstanceIDTokenFetchOperation.h */, + 6980A1AC965054D472E7A754E0433A29 /* FIRInstanceIDTokenFetchOperation.m */, + D96D27B23ED456F3F91185B0591AA335 /* FIRInstanceIDTokenInfo.h */, + 93FB1D3F1A434DADF7E8F2E0DCDF6CB4 /* FIRInstanceIDTokenInfo.m */, + 8F7812E7B55B6200DF820251D9207F56 /* FIRInstanceIDTokenManager.h */, + F38AE2C8EE17A7657996595BFC9ABE11 /* FIRInstanceIDTokenManager.m */, + 39DE45AADB09BA42C0F26F4A09E7BBEE /* FIRInstanceIDTokenOperation.h */, + 571008D58CCCDD41BFC7992FC649BC4D /* FIRInstanceIDTokenOperation.m */, + 29BA6B4B3A27AAF40CE91D7F460B4C09 /* FIRInstanceIDTokenOperation+Private.h */, + 85599DEC750671534D9582042B8D286B /* FIRInstanceIDTokenStore.h */, + 6DF78EC6817986F64D37F229896D32E8 /* FIRInstanceIDTokenStore.m */, + B5F6A2DBC7DB5FAD70AD67EE2F401420 /* FIRInstanceIDURLQueryItem.h */, + 03CAC1CE04C75824406C3CB91E3A6CB2 /* FIRInstanceIDURLQueryItem.m */, + 9C252AD090AF9844AB7721AC0D603C74 /* FIRInstanceIDUtilities.h */, + DFE9A713C35D87D3276D66BDED99B818 /* FIRInstanceIDUtilities.m */, + 3693350ACB41663D2E80F539D7E5C50E /* FIRInstanceIDVersionUtilities.h */, + E83DAD359A051E38E0DCAAC7824D73BE /* FIRInstanceIDVersionUtilities.m */, + 54C93ABAE1D2705DC66F2D99C41F09C5 /* NSError+FIRInstanceID.h */, + FB866184F9DB8C552543637E0980B4FA /* NSError+FIRInstanceID.m */, + C867A118C912B19773323DA8F4417A47 /* Support Files */, ); - name = MethodSwizzler; + name = FirebaseInstanceID; + path = FirebaseInstanceID; sourceTree = "<group>"; }; - D0F07F57E977CDCCD45C86C6FBB4DB8D /* decode */ = { + D8E1E1B643379135870A88FE866DF9BE /* GTMSessionFetcher */ = { isa = PBXGroup; children = ( + 13A669684406EF1322D659F985E365A0 /* Core */, + AFD5746B3322A5A7717FD58DEC1AB0C6 /* Full */, + 50EE0F54CBEA07DF918CEAA8C4E49506 /* Support Files */, ); - name = decode; + name = GTMSessionFetcher; + path = GTMSessionFetcher; sourceTree = "<group>"; }; - D7AD9E405FB6D503AA3B8482847C0B88 /* Support Files */ = { + DB6645AFD4BCB1190B7697DA96230830 /* Frameworks */ = { isa = PBXGroup; children = ( - A01DF1FAEB9BDFCF3637999217E24CD7 /* GoogleUtilities.modulemap */, - 49168AFB20C78AD0A895694EF3D63CC4 /* GoogleUtilities.xcconfig */, - AD20E02685107AFBC498147B840A6226 /* GoogleUtilities-dummy.m */, - AB268DEC9599640A23DDA856C934C565 /* GoogleUtilities-Info.plist */, - 3C5AB1B9901286B399B7703CDFF3E673 /* GoogleUtilities-prefix.pch */, - A8182678D4ABD0F98642AD7406D1BCB2 /* GoogleUtilities-umbrella.h */, + 5B0CAE77A1601E438C553BCD88A71A50 /* GTMSessionFetcher.framework */, + 5DFB3D0FCDA8985A1D96E5FDC266EAE2 /* iOS */, ); - name = "Support Files"; - path = "../Target Support Files/GoogleUtilities"; + name = Frameworks; sourceTree = "<group>"; }; E201F6F1A7795FB7DB590924C510BDB4 /* Targets Support Files */ = { @@ -1008,69 +1299,73 @@ name = "Targets Support Files"; sourceTree = "<group>"; }; - E465C9C9A37D3AAF50EE35B8D991B0C1 /* Support Files */ = { + E34D4A6C379908310BCC0023B51767E5 /* Support Files */ = { isa = PBXGroup; children = ( - 37267C08CB7A9EA152D4AD641442A31A /* GoogleAppMeasurement.xcconfig */, + C2BF9601E45E61A49D75F7726674EC21 /* GoogleUtilities.modulemap */, + 81BAD96EB0AFAC3A8B603522A2FD846D /* GoogleUtilities.xcconfig */, + BF158F186A2E83AA478BC8C6ECB96907 /* GoogleUtilities-dummy.m */, + 415002A8AF1CDA669462CD54B22427D1 /* GoogleUtilities-Info.plist */, + 420B98A22418457646F0C81E28AEC1F8 /* GoogleUtilities-prefix.pch */, + 84B75F5A2DD728216A8F32ED58EB0EEC /* GoogleUtilities-umbrella.h */, ); name = "Support Files"; - path = "../Target Support Files/GoogleAppMeasurement"; + path = "../Target Support Files/GoogleUtilities"; sourceTree = "<group>"; }; - E47968FDAD5BDD7F20DB6D52472B5D29 /* Products */ = { + E51C279489632B4AE3A37D169F20A5FE /* Firebase */ = { isa = PBXGroup; children = ( - 538DA2323E03A0DB8EB8C32F053A1AA9 /* FirebaseCore.framework */, - 6A72A94599CE16306189626A8244DB0B /* FirebaseInstanceID.framework */, - 6C897EBDA102E414BA5982CD110CF5EA /* GoogleUtilities.framework */, - F63E247523E2811DA8F4C96C9D832E48 /* nanopb.framework */, - 5089053A2D4D7A353FEF844FC3C7DC8F /* Pods_TeachersAssistant.framework */, - 828E16FFF2FF45FB2837CBB01DC1F03A /* Pods_TeachersAssistantTests.framework */, + EE77EDCD35852D3D8DAA905696217D4F /* CoreOnly */, + 95D80BD57F09D32EBA1416C100D6161A /* Support Files */, ); - name = Products; + name = Firebase; + path = Firebase; sourceTree = "<group>"; }; - E5D6002B5E9D0AB903235E8741F4E21C /* Pods */ = { + ED5AEF75C2F9AC611441207C0681F931 /* NSData+zlib */ = { isa = PBXGroup; children = ( - 4C2424D8DD47BC1A281492A84B89457F /* Firebase */, - 459B3BB97550355500B234851908FA0C /* FirebaseAnalytics */, - 0A0D42F1BC6E98D59A8149315B27E620 /* FirebaseCore */, - 8C70AF138CF55CC76B32F93E3E170B26 /* FirebaseInstanceID */, - 3E0CF83956D585C0C5A074BA9C974349 /* GoogleAppMeasurement */, - 9AFA54316C8D6149E7425B0C64723281 /* GoogleUtilities */, - 81E55F5D3C08B8318CC6A7C7D7B539F7 /* nanopb */, + F0D1F45EB0BE7940F2B0A3B152F0BAF0 /* GULNSData+zlib.h */, + 09B65EA9CD18054C3731D78BF06AFBBC /* GULNSData+zlib.m */, ); - name = Pods; + name = "NSData+zlib"; sourceTree = "<group>"; }; - E9D7766E604C6484F91D520CBEB15F72 /* Logger */ = { + ED948ECEDEC70E540465331CF6AD793B /* encode */ = { isa = PBXGroup; children = ( - A3E64E6865163D74A4A9C1720F021176 /* GULLogger.h */, - 2A95F0619B59607B38D60DFC6A5815C4 /* GULLogger.m */, - 8BE349DEB3CC69BD666C0E30A25A632E /* GULLoggerLevel.h */, ); - name = Logger; + name = encode; sourceTree = "<group>"; }; - ECBE037778614C9F620F12F5D0FE936A /* AppDelegateSwizzler */ = { + EE77EDCD35852D3D8DAA905696217D4F /* CoreOnly */ = { isa = PBXGroup; children = ( - 65446BC662392EE8067E81346AB6D2E0 /* GULAppDelegateSwizzler.h */, - 3BFE4C8CE0104CF4C505FE3DF5422F51 /* GULAppDelegateSwizzler.m */, - 322F1229F2E3E83A51DF4376E1C3521A /* GULAppDelegateSwizzler_Private.h */, - 807CA56F6A01FAE1DD7905DCD950E8CB /* GULLoggerCodes.h */, + C9D528AA2118222E51B39583783D3824 /* Firebase.h */, ); - name = AppDelegateSwizzler; + name = CoreOnly; + sourceTree = "<group>"; + }; + F2A8EA43702E1C27A51E54A819C55FE7 /* Products */ = { + isa = PBXGroup; + children = ( + 017F9C2BF8786F97CC75BB1728EC1B83 /* FirebaseCore.framework */, + 583BBC1D30E637FD9C0BB1442D90C0D2 /* FirebaseInstanceID.framework */, + 115AA047B525A10FCEE3748D755468AF /* GoogleAPIClientForREST.framework */, + DB136240D29989E1D1B5EDBB26789605 /* GoogleUtilities.framework */, + FBA87A61F13A66578C76CA164240E08E /* GTMSessionFetcher.framework */, + 5D066DD9A210B03A0B9E0DAB7683DE7B /* nanopb.framework */, + 368ACD55DE3CCC50F08A01196D3AAF5D /* Pods_TeachersAssistant.framework */, + 44BEC70CEE7D3F3A73A2AAFC74050F84 /* Pods_TeachersAssistantTests.framework */, + ); + name = Products; sourceTree = "<group>"; }; - FD6C725FFDC376C9FD970F4EA44D6827 /* Frameworks */ = { + FB89EE033F8E78976FB7923B48CEB037 /* Frameworks */ = { isa = PBXGroup; children = ( - 6E083BECC4ABE126127F7F318F3981A7 /* FIRAnalyticsConnector.framework */, - 79BA4CC3A4E8659F47EECDA56535E22C /* FirebaseAnalytics.framework */, - FBA740C4511FAFFE64FCADCFA8D55BA5 /* FirebaseCoreDiagnostics.framework */, + ADDFACA311CA984C4ED942D7153C7BBC /* GoogleAppMeasurement.framework */, ); name = Frameworks; sourceTree = "<group>"; @@ -1078,6 +1373,33 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 7651B2F44918358BF29B9101BF7FC04A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DE33BF21B7C31BD920D551FAAB7EA056 /* GoogleAPIClientForREST-umbrella.h in Headers */, + ABA437DFA30B41D0B77E0592682739FE /* GTLRBase64.h in Headers */, + 69F46ADB15D518B3F0323377AFEE974E /* GTLRBatchQuery.h in Headers */, + D9CAB0323F055B25038E725BDF34909A /* GTLRBatchResult.h in Headers */, + 8E0A60B68DDAD2843111B1C139A1C29E /* GTLRDateTime.h in Headers */, + 730879B4705522202D09520FBDEE11A5 /* GTLRDefines.h in Headers */, + 63CBC280596E3A453DF91743EBBC898A /* GTLRDuration.h in Headers */, + 7D1EE73692570398E3148C707E474265 /* GTLRErrorObject.h in Headers */, + 3C650CC5DFF8B2369B0326CF9EFA3B74 /* GTLRFramework.h in Headers */, + D79F7227DECCB6A36A093DC50BDEE07E /* GTLRObject.h in Headers */, + 8F07CBB8F46A54B2AE38082096AAC033 /* GTLRQuery.h in Headers */, + D90D2E30A9BE48C385E0B6F2A40A8924 /* GTLRRuntimeCommon.h in Headers */, + E16C9C03AFF3F2637540647FB777E3A0 /* GTLRService.h in Headers */, + 689CA019D0B329049875601AD99A0C04 /* GTLRSheets.h in Headers */, + 57C2471A37561E07AE381E7E346F77B3 /* GTLRSheetsObjects.h in Headers */, + F4E0A3D1D35780BFF837007FA581A4F0 /* GTLRSheetsQuery.h in Headers */, + 529E198E8565F59B3BE1364522AAEC87 /* GTLRSheetsService.h in Headers */, + 0DC5A104F5C14D11C8CA9F2EBEB41725 /* GTLRUploadParameters.h in Headers */, + D90D03372B914B0F17346CFA70DE4BEC /* GTLRURITemplate.h in Headers */, + 4F6EF06D616D2AEDAEAC81ECDE2C488E /* GTLRUtilities.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 87C026EB9FF5BAE426AB2D038DE18A0C /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1105,14 +1427,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 8CF03D1B14D1348C25BA2626A071DE99 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D47EEC76008C543C3E272E8D810E1F3D /* Pods-TeachersAssistant-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 975138D412B64947404A4BBBEC1120B8 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1125,6 +1439,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9EE6B9D2BCE26D1D034F17B647CE0CE4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57C854E399EE2DDEA121CD54EE49CB07 /* Pods-TeachersAssistant-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B0C6AA217B2F7C5F9FB7BB5807636FE9 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1204,6 +1526,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F94EBFB7D9EA26CBCA328213A9F6C50F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4113F1C1982CB9F61B0877D8E2A3C982 /* GTMGatherInputStream.h in Headers */, + 38AE6B968DC0DE94A749503C4FF43E51 /* GTMMIMEDocument.h in Headers */, + EA0876B4F2B40E37E01E0D9F96BA34B3 /* GTMReadMonitorInputStream.h in Headers */, + DE84A495B46D938B298901309B53C60A /* GTMSessionFetcher-umbrella.h in Headers */, + B1A5C2FE1615E297E4A245C34B7F6762 /* GTMSessionFetcher.h in Headers */, + A3A05DE0E3AEB3299F7606F0F8BD7756 /* GTMSessionFetcherLogging.h in Headers */, + 102658B33FEC7C008C1C8229F40351F9 /* GTMSessionFetcherService.h in Headers */, + 90B3B87D94A9F9F6A90C2222E7516DC9 /* GTMSessionUploadFetcher.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -1223,7 +1560,7 @@ ); name = FirebaseCore; productName = FirebaseCore; - productReference = 538DA2323E03A0DB8EB8C32F053A1AA9 /* FirebaseCore.framework */; + productReference = 017F9C2BF8786F97CC75BB1728EC1B83 /* FirebaseCore.framework */; productType = "com.apple.product-type.framework"; }; 3C6A9BF574C3488966C92C6A9B93CA8C /* FirebaseInstanceID */ = { @@ -1243,7 +1580,26 @@ ); name = FirebaseInstanceID; productName = FirebaseInstanceID; - productReference = 6A72A94599CE16306189626A8244DB0B /* FirebaseInstanceID.framework */; + productReference = 583BBC1D30E637FD9C0BB1442D90C0D2 /* FirebaseInstanceID.framework */; + productType = "com.apple.product-type.framework"; + }; + 772340DB04E857CA1ABBDB7EC1CA7079 /* GoogleAPIClientForREST */ = { + isa = PBXNativeTarget; + buildConfigurationList = 89E0E08DEE3712E3F0EB975775425754 /* Build configuration list for PBXNativeTarget "GoogleAPIClientForREST" */; + buildPhases = ( + 7651B2F44918358BF29B9101BF7FC04A /* Headers */, + 34D7FA85EF968F82B524206A7BEFB71F /* Sources */, + 5D2361E98237FB7C772C750B91DB5F05 /* Frameworks */, + D7AA918ECAFF58E36CFFDEDDFB63C960 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7C898B425FA4DA448FB2CF9AFF680EB3 /* PBXTargetDependency */, + ); + name = GoogleAPIClientForREST; + productName = GoogleAPIClientForREST; + productReference = 115AA047B525A10FCEE3748D755468AF /* GoogleAPIClientForREST.framework */; productType = "com.apple.product-type.framework"; }; 9115383C4EEFD422CC2FA6BC0AEEF0EE /* Pods-TeachersAssistantTests */ = { @@ -1262,50 +1618,70 @@ ); name = "Pods-TeachersAssistantTests"; productName = "Pods-TeachersAssistantTests"; - productReference = 828E16FFF2FF45FB2837CBB01DC1F03A /* Pods_TeachersAssistantTests.framework */; + productReference = 44BEC70CEE7D3F3A73A2AAFC74050F84 /* Pods_TeachersAssistantTests.framework */; productType = "com.apple.product-type.framework"; }; - D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */ = { + C1DBAA447353B7E3A4A495D068C464D8 /* Pods-TeachersAssistant */ = { isa = PBXNativeTarget; - buildConfigurationList = BC00811E082341577790995EE25EA091 /* Build configuration list for PBXNativeTarget "GoogleUtilities" */; + buildConfigurationList = 4E2FA166349EB1570B43A20CD7C75E62 /* Build configuration list for PBXNativeTarget "Pods-TeachersAssistant" */; buildPhases = ( - 87C026EB9FF5BAE426AB2D038DE18A0C /* Headers */, - 0089DDDA3A25B67F94AF09C8C8811837 /* Sources */, - 2AB82CCC1FE772DB4095EA633511AF3C /* Frameworks */, - 55761CA536EE839D1D94DFD593A64BE0 /* Resources */, + 9EE6B9D2BCE26D1D034F17B647CE0CE4 /* Headers */, + 31ABA1B93F9C30E2DA6887C83DDF6EC4 /* Sources */, + A38A6054F26C426AE2C0C0EDAD0008FC /* Frameworks */, + 3D21DC586C06817D3BFE1CEABF727378 /* Resources */, ); buildRules = ( ); dependencies = ( + 5375E58AA18148CDB7F1E4D467ED33C7 /* PBXTargetDependency */, + FD179E7758867534E6581E867B338091 /* PBXTargetDependency */, + A5C6C41C0E1E593E24CC18D4327294F0 /* PBXTargetDependency */, + D864BA67E5C411873861098E5DF40E5B /* PBXTargetDependency */, + 1B066CE6895A68932D1E3FB0E6D60288 /* PBXTargetDependency */, + EB950109F56ACBB3F970D78AF505E00B /* PBXTargetDependency */, + CED6FBDC7E35B5A1EDAC22DF1D0066FC /* PBXTargetDependency */, + CCD4F9B43A101889457818D0E194D031 /* PBXTargetDependency */, + 5109C054A0FD609A3CF3986B9B8C9177 /* PBXTargetDependency */, ); - name = GoogleUtilities; - productName = GoogleUtilities; - productReference = 6C897EBDA102E414BA5982CD110CF5EA /* GoogleUtilities.framework */; + name = "Pods-TeachersAssistant"; + productName = "Pods-TeachersAssistant"; + productReference = 368ACD55DE3CCC50F08A01196D3AAF5D /* Pods_TeachersAssistant.framework */; productType = "com.apple.product-type.framework"; }; - E6548F36829F0F6D877D829B97A54A90 /* Pods-TeachersAssistant */ = { + D811FCD6F8EBFE221BF9D923CEFB5CE8 /* GTMSessionFetcher */ = { isa = PBXNativeTarget; - buildConfigurationList = BA9C47BF66988F403C678B830CF62681 /* Build configuration list for PBXNativeTarget "Pods-TeachersAssistant" */; + buildConfigurationList = A0F94913EC690DC5D7F6D981D004310A /* Build configuration list for PBXNativeTarget "GTMSessionFetcher" */; buildPhases = ( - 8CF03D1B14D1348C25BA2626A071DE99 /* Headers */, - A6911D3BDC47EC442FA8D885BB3ED959 /* Sources */, - BEFF10831A398C90B88A222D755D5724 /* Frameworks */, - 815C25029F4356C39AA9CA071D3099E6 /* Resources */, + F94EBFB7D9EA26CBCA328213A9F6C50F /* Headers */, + F8054326668F148082A04F36EA9270E4 /* Sources */, + 285219F57C35AF74521FE10278556E39 /* Frameworks */, + C559FBE113680A38D58FBC77BF6FA323 /* Resources */, ); buildRules = ( ); dependencies = ( - CDCA25E5A3F9D918483C7312D40770C6 /* PBXTargetDependency */, - A6A631C311E3194B0CD2AC3DFA033726 /* PBXTargetDependency */, - C6AEE0BE7308E5C70827CD09F42FC732 /* PBXTargetDependency */, - 2425D5852A51AE60026D0933B0721888 /* PBXTargetDependency */, - 7465EF98C9D88618B55B7D03473071E1 /* PBXTargetDependency */, - D293FBE1FF2A323B648346C961E980C5 /* PBXTargetDependency */, - C0EF389F0E06B92D88E20458C65711CD /* PBXTargetDependency */, ); - name = "Pods-TeachersAssistant"; - productName = "Pods-TeachersAssistant"; - productReference = 5089053A2D4D7A353FEF844FC3C7DC8F /* Pods_TeachersAssistant.framework */; + name = GTMSessionFetcher; + productName = GTMSessionFetcher; + productReference = FBA87A61F13A66578C76CA164240E08E /* GTMSessionFetcher.framework */; + productType = "com.apple.product-type.framework"; + }; + D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC00811E082341577790995EE25EA091 /* Build configuration list for PBXNativeTarget "GoogleUtilities" */; + buildPhases = ( + 87C026EB9FF5BAE426AB2D038DE18A0C /* Headers */, + 0089DDDA3A25B67F94AF09C8C8811837 /* Sources */, + 2AB82CCC1FE772DB4095EA633511AF3C /* Frameworks */, + 55761CA536EE839D1D94DFD593A64BE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GoogleUtilities; + productName = GoogleUtilities; + productReference = DB136240D29989E1D1B5EDBB26789605 /* GoogleUtilities.framework */; productType = "com.apple.product-type.framework"; }; E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */ = { @@ -1323,7 +1699,7 @@ ); name = nanopb; productName = nanopb; - productReference = F63E247523E2811DA8F4C96C9D832E48 /* nanopb.framework */; + productReference = 5D066DD9A210B03A0B9E0DAB7683DE7B /* nanopb.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ @@ -1343,7 +1719,7 @@ en, ); mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = E47968FDAD5BDD7F20DB6D52472B5D29 /* Products */; + productRefGroup = F2A8EA43702E1C27A51E54A819C55FE7 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -1351,10 +1727,12 @@ 232D00D8ED7797390FB38004DE01723B /* FirebaseAnalytics */, 01B53B6A43CBD6D4022A361BBFCCE665 /* FirebaseCore */, 3C6A9BF574C3488966C92C6A9B93CA8C /* FirebaseInstanceID */, + 772340DB04E857CA1ABBDB7EC1CA7079 /* GoogleAPIClientForREST */, 57B9E0A892EAB5C13D4AE7D4B1DE0C16 /* GoogleAppMeasurement */, D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */, + D811FCD6F8EBFE221BF9D923CEFB5CE8 /* GTMSessionFetcher */, E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */, - E6548F36829F0F6D877D829B97A54A90 /* Pods-TeachersAssistant */, + C1DBAA447353B7E3A4A495D068C464D8 /* Pods-TeachersAssistant */, 9115383C4EEFD422CC2FA6BC0AEEF0EE /* Pods-TeachersAssistantTests */, ); }; @@ -1368,14 +1746,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 55761CA536EE839D1D94DFD593A64BE0 /* Resources */ = { + 3D21DC586C06817D3BFE1CEABF727378 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 815C25029F4356C39AA9CA071D3099E6 /* Resources */ = { + 55761CA536EE839D1D94DFD593A64BE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -1396,6 +1774,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C559FBE113680A38D58FBC77BF6FA323 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C7AA1C6CBDCA2488533EB20B524FF83F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1403,6 +1788,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D7AA918ECAFF58E36CFFDEDDFB63C960 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1482,6 +1874,39 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 31ABA1B93F9C30E2DA6887C83DDF6EC4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C191B7AFCB86D9C55E552A9432A08AA5 /* Pods-TeachersAssistant-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 34D7FA85EF968F82B524206A7BEFB71F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 93F238A3CBF92469EAB23AA952814B9E /* GoogleAPIClientForREST-dummy.m in Sources */, + 9597B01BCD32158B7674DA270A77A05D /* GTLRBase64.m in Sources */, + E32F693371E7272421A8F5ABE4C7D05B /* GTLRBatchQuery.m in Sources */, + BAEA0165416D2478BB040F1584A157E9 /* GTLRBatchResult.m in Sources */, + 26FE54055CE40CA8B7B8556919C21149 /* GTLRDateTime.m in Sources */, + 6B80256F23FA1918E977CEDE91CE3697 /* GTLRDuration.m in Sources */, + 5D2B3289AB0C9082A36E106B6FC1A1C9 /* GTLRErrorObject.m in Sources */, + 09E4B4EBE4FBA72C30564F3E3DA2081F /* GTLRFramework.m in Sources */, + 564FB83D43683D79E3DF91EC972184D8 /* GTLRObject.m in Sources */, + 9BBC4EFFC569655FBA0A52C627D674F0 /* GTLRQuery.m in Sources */, + D775E01ED0653D1AA554B9D50E9C276E /* GTLRRuntimeCommon.m in Sources */, + 6D61C466E6E69A842B65285DE2261213 /* GTLRService.m in Sources */, + E9D3144ECE2DB2BCCA436A874DE7EC89 /* GTLRSheetsObjects.m in Sources */, + 6DEE0C82F00E41474F70E54D39C1337D /* GTLRSheetsQuery.m in Sources */, + 2ECE32250F9379832BA31C5788E02EBC /* GTLRSheetsService.m in Sources */, + CF12D67880876BC3C2360BB7F3C792ED /* GTLRUploadParameters.m in Sources */, + DF87CF5ECCAE4379FB2B17ACD054F1F2 /* GTLRURITemplate.m in Sources */, + 656F3D54ACD64C547F9594F124FA2D5E /* GTLRUtilities.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 700851C9880F6C2451244067429289CA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1493,11 +1918,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - A6911D3BDC47EC442FA8D885BB3ED959 /* Sources */ = { + F8054326668F148082A04F36EA9270E4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 45E1670E670793EE76B9327441B0BC32 /* Pods-TeachersAssistant-dummy.m in Sources */, + D044612547CC780C57BA3DDF204F4550 /* GTMGatherInputStream.m in Sources */, + 1752930EA1F0BD6E9408C8298F178491 /* GTMMIMEDocument.m in Sources */, + CE0AF5ABE638F0F58F8DC3D81B7E3100 /* GTMReadMonitorInputStream.m in Sources */, + 60070AAFBCB471BDF2A5903E309BF939 /* GTMSessionFetcher-dummy.m in Sources */, + F1D4FF2068E4D0B9395A2B253CF7098C /* GTMSessionFetcher.m in Sources */, + 55E0ADCA640BB3322559794AED57F451 /* GTMSessionFetcherLogging.m in Sources */, + 5992ECFCB7E99F063A2D36AE73F44D60 /* GTMSessionFetcherService.m in Sources */, + 2300C13FB133313D32DFFB0D57C25ECC /* GTMSessionUploadFetcher.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1524,11 +1956,11 @@ target = 01B53B6A43CBD6D4022A361BBFCCE665 /* FirebaseCore */; targetProxy = 0ECB4C54EED84F5258E41AFD4657F11F /* PBXContainerItemProxy */; }; - 2425D5852A51AE60026D0933B0721888 /* PBXTargetDependency */ = { + 1B066CE6895A68932D1E3FB0E6D60288 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = FirebaseInstanceID; - target = 3C6A9BF574C3488966C92C6A9B93CA8C /* FirebaseInstanceID */; - targetProxy = 6E678B000225AD45F3BA9059ED4E8485 /* PBXContainerItemProxy */; + name = GTMSessionFetcher; + target = D811FCD6F8EBFE221BF9D923CEFB5CE8 /* GTMSessionFetcher */; + targetProxy = 98553A26A4F1509909FEE91EA0162432 /* PBXContainerItemProxy */; }; 3BDD26DF1C76A2717767412BFEFD633E /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -1536,11 +1968,17 @@ target = E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */; targetProxy = C6318E60C9E68C5F678F7ADDF357AED8 /* PBXContainerItemProxy */; }; - 7465EF98C9D88618B55B7D03473071E1 /* PBXTargetDependency */ = { + 5109C054A0FD609A3CF3986B9B8C9177 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = GoogleAppMeasurement; - target = 57B9E0A892EAB5C13D4AE7D4B1DE0C16 /* GoogleAppMeasurement */; - targetProxy = FB82F0ED3E43996A5BB572C2CDA1A7C3 /* PBXContainerItemProxy */; + name = nanopb; + target = E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */; + targetProxy = 63982F044D1394A02009890DC4D11191 /* PBXContainerItemProxy */; + }; + 5375E58AA18148CDB7F1E4D467ED33C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Firebase; + target = 799B29F9D6DCE28B98CC259440382F20 /* Firebase */; + targetProxy = 0F60F822741F17E09A70AC758014F400 /* PBXContainerItemProxy */; }; 7AEC0D15EF11C1415A94D769184AD812 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -1554,6 +1992,12 @@ target = D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */; targetProxy = 53E2A1BD19729C2293AB46582C686251 /* PBXContainerItemProxy */; }; + 7C898B425FA4DA448FB2CF9AFF680EB3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GTMSessionFetcher; + target = D811FCD6F8EBFE221BF9D923CEFB5CE8 /* GTMSessionFetcher */; + targetProxy = F57E55D97C9AF0B37D53F628359ABA5F /* PBXContainerItemProxy */; + }; 9C390500C3C568F59A8589C455BFF4D5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = FirebaseInstanceID; @@ -1566,11 +2010,11 @@ target = E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */; targetProxy = DF12C5D7BB68C2724D2F39A531F2A52A /* PBXContainerItemProxy */; }; - A6A631C311E3194B0CD2AC3DFA033726 /* PBXTargetDependency */ = { + A5C6C41C0E1E593E24CC18D4327294F0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = FirebaseAnalytics; - target = 232D00D8ED7797390FB38004DE01723B /* FirebaseAnalytics */; - targetProxy = 5863AE56A2562B5D081E6742D63982F4 /* PBXContainerItemProxy */; + name = FirebaseCore; + target = 01B53B6A43CBD6D4022A361BBFCCE665 /* FirebaseCore */; + targetProxy = 1978C06F97680C1C8CFE2AC9F848F053 /* PBXContainerItemProxy */; }; AA9052A974DA4ECF27CC38A7633849E0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -1590,18 +2034,6 @@ target = D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */; targetProxy = 54A7BA384E80D5DB0269C827877FE175 /* PBXContainerItemProxy */; }; - C0EF389F0E06B92D88E20458C65711CD /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = nanopb; - target = E93C48A48FB03EA19C4F756B97B5F1D3 /* nanopb */; - targetProxy = DC109F4C3DFF347305FC87301148EE99 /* PBXContainerItemProxy */; - }; - C6AEE0BE7308E5C70827CD09F42FC732 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FirebaseCore; - target = 01B53B6A43CBD6D4022A361BBFCCE665 /* FirebaseCore */; - targetProxy = AA9AF4CB5DB51B15FD03B4B06A1E364F /* PBXContainerItemProxy */; - }; CA20CC0CC8595F02B384BCF03BBE9452 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = FirebaseCore; @@ -1614,24 +2046,42 @@ target = 01B53B6A43CBD6D4022A361BBFCCE665 /* FirebaseCore */; targetProxy = F6A14184DE3C02C257A7298719E4FD9B /* PBXContainerItemProxy */; }; - CDCA25E5A3F9D918483C7312D40770C6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Firebase; - target = 799B29F9D6DCE28B98CC259440382F20 /* Firebase */; - targetProxy = AD3680B60351F3B4F1A057686D61E059 /* PBXContainerItemProxy */; - }; - D293FBE1FF2A323B648346C961E980C5 /* PBXTargetDependency */ = { + CCD4F9B43A101889457818D0E194D031 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = GoogleUtilities; target = D9A2B7F6350AE8AB9AAFF5A9395AD63C /* GoogleUtilities */; - targetProxy = 402E60D1719D9B9FF37FA3105F6DCF2D /* PBXContainerItemProxy */; + targetProxy = 54C6A825F7C3057AC5EB47B5EDA879F3 /* PBXContainerItemProxy */; + }; + CED6FBDC7E35B5A1EDAC22DF1D0066FC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GoogleAppMeasurement; + target = 57B9E0A892EAB5C13D4AE7D4B1DE0C16 /* GoogleAppMeasurement */; + targetProxy = 475E73100BA3CA8F024336F74263C678 /* PBXContainerItemProxy */; + }; + D864BA67E5C411873861098E5DF40E5B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FirebaseInstanceID; + target = 3C6A9BF574C3488966C92C6A9B93CA8C /* FirebaseInstanceID */; + targetProxy = 94497EC05AB24679A89D934FDF900750 /* PBXContainerItemProxy */; }; EB4921EFA7B1ACE9CB14B01E8D6AA7B9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "Pods-TeachersAssistant"; - target = E6548F36829F0F6D877D829B97A54A90 /* Pods-TeachersAssistant */; + target = C1DBAA447353B7E3A4A495D068C464D8 /* Pods-TeachersAssistant */; targetProxy = CBA0C327941F96015A5D0C2A0F65E1D9 /* PBXContainerItemProxy */; }; + EB950109F56ACBB3F970D78AF505E00B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GoogleAPIClientForREST; + target = 772340DB04E857CA1ABBDB7EC1CA7079 /* GoogleAPIClientForREST */; + targetProxy = B82C96A38C8E60FE2CEB597BB1AC15D7 /* PBXContainerItemProxy */; + }; + FD179E7758867534E6581E867B338091 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FirebaseAnalytics; + target = 232D00D8ED7797390FB38004DE01723B /* FirebaseAnalytics */; + targetProxy = 449060759393A041D614674D0E5B2085 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1701,7 +2151,7 @@ }; 16393E98E7D33B078F35DAD0BF562519 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 15E28D734540855F231814108CF62173 /* FirebaseAnalytics.xcconfig */; + baseConfigurationReference = E8ABC734EC5D76410142B606FB284CB9 /* FirebaseAnalytics.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -1714,7 +2164,7 @@ }; 1AF9C8CAFEB4999C4459A2A076CB87EA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 49168AFB20C78AD0A895694EF3D63CC4 /* GoogleUtilities.xcconfig */; + baseConfigurationReference = 81BAD96EB0AFAC3A8B603522A2FD846D /* GoogleUtilities.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1743,23 +2193,41 @@ }; name = Debug; }; - 31667BB5CFC7B8D4C8E24E1A05DE6F20 /* Release */ = { + 20A9A4E87D934379BD3E45E876B82100 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 15E28D734540855F231814108CF62173 /* FirebaseAnalytics.xcconfig */; + baseConfigurationReference = C6BA66AB7BE9C1B35D1FEEA4378F5D1C /* GoogleAPIClientForREST.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap"; + PRODUCT_MODULE_NAME = GoogleAPIClientForREST; + PRODUCT_NAME = GoogleAPIClientForREST; SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; }; name = Release; }; - 3533CB8438160A7CEA06ADAB9FF2F53C /* Release */ = { + 310A7C2D9EB41AC5A1EA77050797C1CF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AFF19F90C766B8C2B9A22DE86B1BFC0F /* FirebaseCore.xcconfig */; + baseConfigurationReference = C6BA66AB7BE9C1B35D1FEEA4378F5D1C /* GoogleAPIClientForREST.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1770,30 +2238,42 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/FirebaseCore/FirebaseCore-Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/FirebaseCore/FirebaseCore.modulemap"; - PRODUCT_MODULE_NAME = FirebaseCore; - PRODUCT_NAME = FirebaseCore; + MODULEMAP_FILE = "Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap"; + PRODUCT_MODULE_NAME = GoogleAPIClientForREST; + PRODUCT_NAME = GoogleAPIClientForREST; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; + name = Debug; + }; + 31667BB5CFC7B8D4C8E24E1A05DE6F20 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E8ABC734EC5D76410142B606FB284CB9 /* FirebaseAnalytics.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; name = Release; }; - 4AD3B98CE340FD2680F647BE5798A4FF /* Debug */ = { + 3533CB8438160A7CEA06ADAB9FF2F53C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 80D0ABF6F63F3EF080A906F8C625412E /* Pods-TeachersAssistant.debug.xcconfig */; + baseConfigurationReference = 66B1B5E0F02389648F7D7F08EE92449E /* FirebaseCore.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -1803,26 +2283,26 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-Info.plist"; + INFOPLIST_FILE = "Target Support Files/FirebaseCore/FirebaseCore-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + MODULEMAP_FILE = "Target Support Files/FirebaseCore/FirebaseCore.modulemap"; + PRODUCT_MODULE_NAME = FirebaseCore; + PRODUCT_NAME = FirebaseCore; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - 551E90F13A8D7C247FCA5FB5887D9D63 /* Release */ = { + 3DDB11D040453637BA5D7B834E0A0DC3 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = A61A5FBB4C4917E85820D47EA05A7A31 /* Pods-TeachersAssistant.release.xcconfig */; buildSettings = { @@ -1858,7 +2338,7 @@ }; 58C4DFAAABA5B1ADD585B075C392989E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B90EE6D4D7C9ADED356D925B56CACE30 /* Firebase.xcconfig */; + baseConfigurationReference = 069518B8B1116A7286048E10777CE4B1 /* Firebase.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -1871,7 +2351,7 @@ }; 7F4D756E8156ED9F30F40B92F6C0AEA4 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5C49A18BA48C07644DDE0215A595578E /* FirebaseInstanceID.xcconfig */; + baseConfigurationReference = 26EEA6C6A294786C4626A5D90B0CC7A8 /* FirebaseInstanceID.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1903,7 +2383,7 @@ }; 8545458A45F896E5207861008FCC568E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AFF19F90C766B8C2B9A22DE86B1BFC0F /* FirebaseCore.xcconfig */; + baseConfigurationReference = 66B1B5E0F02389648F7D7F08EE92449E /* FirebaseCore.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1932,9 +2412,42 @@ }; name = Debug; }; + 861A432984C82E905B9F26DA86C47709 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 80D0ABF6F63F3EF080A906F8C625412E /* Pods-TeachersAssistant.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; 9D31AF365B999422C95ED9755B2E8F0F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B90EE6D4D7C9ADED356D925B56CACE30 /* Firebase.xcconfig */; + baseConfigurationReference = 069518B8B1116A7286048E10777CE4B1 /* Firebase.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -2041,7 +2554,7 @@ }; B152863C5CAB9435224A4B4B99FB9056 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 49168AFB20C78AD0A895694EF3D63CC4 /* GoogleUtilities.xcconfig */; + baseConfigurationReference = 81BAD96EB0AFAC3A8B603522A2FD846D /* GoogleUtilities.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -2073,7 +2586,7 @@ }; D39AE346BB890EE92D52B4365CF97C8E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 37267C08CB7A9EA152D4AD641442A31A /* GoogleAppMeasurement.xcconfig */; + baseConfigurationReference = 8416808131C45D6E3E6BC32C40C76A82 /* GoogleAppMeasurement.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -2087,7 +2600,7 @@ }; D3FA7F3570E2CF3159A5CBBF6D6D03EB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 37267C08CB7A9EA152D4AD641442A31A /* GoogleAppMeasurement.xcconfig */; + baseConfigurationReference = 8416808131C45D6E3E6BC32C40C76A82 /* GoogleAppMeasurement.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -2098,9 +2611,40 @@ }; name = Debug; }; + D6EB5AEAC28EC7AAE85BD1E933FB690C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A22E7D0390892ECB350FFF998A271D0 /* GTMSessionFetcher.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher.modulemap"; + PRODUCT_MODULE_NAME = GTMSessionFetcher; + PRODUCT_NAME = GTMSessionFetcher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; D882A20A5AA7EA50AAF3215480D4722C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E4314890709AC882167685A1D464BD4A /* nanopb.xcconfig */; + baseConfigurationReference = ADF1B1D4D72635AB6E8D3C36D8D89A6C /* nanopb.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -2164,9 +2708,41 @@ }; name = Release; }; + F587407A3C5808F95C6CB5151809FBF9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A22E7D0390892ECB350FFF998A271D0 /* GTMSessionFetcher.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/GTMSessionFetcher/GTMSessionFetcher.modulemap"; + PRODUCT_MODULE_NAME = GTMSessionFetcher; + PRODUCT_NAME = GTMSessionFetcher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; FB0FE76ECC42F40FB47968B8BBDE0E0B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5C49A18BA48C07644DDE0215A595578E /* FirebaseInstanceID.xcconfig */; + baseConfigurationReference = 26EEA6C6A294786C4626A5D90B0CC7A8 /* FirebaseInstanceID.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -2197,7 +2773,7 @@ }; FFB7DBE7747FE7B036A3959B526D616C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E4314890709AC882167685A1D464BD4A /* nanopb.xcconfig */; + baseConfigurationReference = ADF1B1D4D72635AB6E8D3C36D8D89A6C /* nanopb.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -2265,6 +2841,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 4E2FA166349EB1570B43A20CD7C75E62 /* Build configuration list for PBXNativeTarget "Pods-TeachersAssistant" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 861A432984C82E905B9F26DA86C47709 /* Debug */, + 3DDB11D040453637BA5D7B834E0A0DC3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 7F06B8F325208699248FA6C351C472D4 /* Build configuration list for PBXNativeTarget "nanopb" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2274,11 +2859,20 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - BA9C47BF66988F403C678B830CF62681 /* Build configuration list for PBXNativeTarget "Pods-TeachersAssistant" */ = { + 89E0E08DEE3712E3F0EB975775425754 /* Build configuration list for PBXNativeTarget "GoogleAPIClientForREST" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 310A7C2D9EB41AC5A1EA77050797C1CF /* Debug */, + 20A9A4E87D934379BD3E45E876B82100 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A0F94913EC690DC5D7F6D981D004310A /* Build configuration list for PBXNativeTarget "GTMSessionFetcher" */ = { isa = XCConfigurationList; buildConfigurations = ( - 4AD3B98CE340FD2680F647BE5798A4FF /* Debug */, - 551E90F13A8D7C247FCA5FB5887D9D63 /* Release */, + D6EB5AEAC28EC7AAE85BD1E933FB690C /* Debug */, + F587407A3C5808F95C6CB5151809FBF9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0930" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForAnalyzing = "YES" + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "D811FCD6F8EBFE221BF9D923CEFB5CE8" + BuildableName = "GTMSessionFetcher.framework" + BlueprintName = "GTMSessionFetcher" + ReferencedContainer = "container:Pods.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + buildConfiguration = "Debug" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES" + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GoogleAPIClientForREST.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/GoogleAPIClientForREST.xcscheme @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0930" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForAnalyzing = "YES" + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "772340DB04E857CA1ABBDB7EC1CA7079" + BuildableName = "GoogleAPIClientForREST.framework" + BlueprintName = "GoogleAPIClientForREST" + ReferencedContainer = "container:Pods.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + buildConfiguration = "Debug" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES" + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/Pods-TeachersAssistant.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/Pods-TeachersAssistant.xcscheme @@ -14,7 +14,7 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "E6548F36829F0F6D877D829B97A54A90" + BlueprintIdentifier = "C1DBAA447353B7E3A4A495D068C464D8" BuildableName = "Pods_TeachersAssistant.framework" BlueprintName = "Pods-TeachersAssistant" ReferencedContainer = "container:Pods.xcodeproj"> @@ -47,7 +47,7 @@ <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "E6548F36829F0F6D877D829B97A54A90" + BlueprintIdentifier = "C1DBAA447353B7E3A4A495D068C464D8" BuildableName = "Pods_TeachersAssistant.framework" BlueprintName = "Pods-TeachersAssistant" ReferencedContainer = "container:Pods.xcodeproj"> diff --git a/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -32,40 +32,54 @@ <key>orderHint</key> <integer>3</integer> </dict> - <key>GoogleAppMeasurement.xcscheme</key> + <key>GTMSessionFetcher.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>7</integer> + </dict> + <key>GoogleAPIClientForREST.xcscheme</key> <dict> <key>isShown</key> <false/> <key>orderHint</key> <integer>4</integer> </dict> - <key>GoogleUtilities.xcscheme</key> + <key>GoogleAppMeasurement.xcscheme</key> <dict> <key>isShown</key> <false/> <key>orderHint</key> <integer>5</integer> </dict> + <key>GoogleUtilities.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>6</integer> + </dict> <key>Pods-TeachersAssistant.xcscheme</key> <dict> <key>isShown</key> <false/> <key>orderHint</key> - <integer>7</integer> + <integer>9</integer> </dict> <key>Pods-TeachersAssistantTests.xcscheme</key> <dict> <key>isShown</key> <false/> <key>orderHint</key> - <integer>8</integer> + <integer>10</integer> </dict> <key>nanopb.xcscheme</key> <dict> <key>isShown</key> <false/> <key>orderHint</key> - <integer>6</integer> + <integer>8</integer> </dict> </dict> <key>SuppressBuildableAutocreation</key> diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-Info.plist b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${PRODUCT_BUNDLE_IDENTIFIER}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.2.1</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>${CURRENT_PROJECT_VERSION}</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-dummy.m b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-dummy.m @@ -0,0 +1,5 @@ +#import <Foundation/Foundation.h> +@interface PodsDummy_GTMSessionFetcher : NSObject +@end +@implementation PodsDummy_GTMSessionFetcher +@end diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-prefix.pch b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import <UIKit/UIKit.h> +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-umbrella.h b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher-umbrella.h @@ -0,0 +1,23 @@ +#ifdef __OBJC__ +#import <UIKit/UIKit.h> +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "GTMSessionFetcher.h" +#import "GTMSessionFetcherLogging.h" +#import "GTMSessionFetcherService.h" +#import "GTMSessionUploadFetcher.h" +#import "GTMGatherInputStream.h" +#import "GTMMIMEDocument.h" +#import "GTMReadMonitorInputStream.h" + +FOUNDATION_EXPORT double GTMSessionFetcherVersionNumber; +FOUNDATION_EXPORT const unsigned char GTMSessionFetcherVersionString[]; + diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.modulemap b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.modulemap @@ -0,0 +1,6 @@ +framework module GTMSessionFetcher { + umbrella header "GTMSessionFetcher-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.xcconfig b/Pods/Target Support Files/GTMSessionFetcher/GTMSessionFetcher.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Security" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/GTMSessionFetcher +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-Info.plist b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${PRODUCT_BUNDLE_IDENTIFIER}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.3.8</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>${CURRENT_PROJECT_VERSION}</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-dummy.m b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-dummy.m @@ -0,0 +1,5 @@ +#import <Foundation/Foundation.h> +@interface PodsDummy_GoogleAPIClientForREST : NSObject +@end +@implementation PodsDummy_GoogleAPIClientForREST +@end diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-prefix.pch b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import <UIKit/UIKit.h> +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-umbrella.h b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST-umbrella.h @@ -0,0 +1,35 @@ +#ifdef __OBJC__ +#import <UIKit/UIKit.h> +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "GTLRDefines.h" +#import "GTLRBatchQuery.h" +#import "GTLRBatchResult.h" +#import "GTLRDateTime.h" +#import "GTLRDuration.h" +#import "GTLRErrorObject.h" +#import "GTLRObject.h" +#import "GTLRQuery.h" +#import "GTLRRuntimeCommon.h" +#import "GTLRService.h" +#import "GTLRUploadParameters.h" +#import "GTLRBase64.h" +#import "GTLRFramework.h" +#import "GTLRURITemplate.h" +#import "GTLRUtilities.h" +#import "GTLRSheets.h" +#import "GTLRSheetsObjects.h" +#import "GTLRSheetsQuery.h" +#import "GTLRSheetsService.h" + +FOUNDATION_EXPORT double GoogleAPIClientForRESTVersionNumber; +FOUNDATION_EXPORT const unsigned char GoogleAPIClientForRESTVersionString[]; + diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap @@ -0,0 +1,6 @@ +framework module GoogleAPIClientForREST { + umbrella header "GoogleAPIClientForREST-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.xcconfig b/Pods/Target Support Files/GoogleAPIClientForREST/GoogleAPIClientForREST.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/GoogleAPIClientForREST +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.markdown b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.markdown @@ -421,6 +421,418 @@ Copyright 2019 Google limitations under the License. +## GTMSessionFetcher + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +## GoogleAPIClientForREST + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + ## GoogleAppMeasurement Copyright 2019 Google diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.plist b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-acknowledgements.plist @@ -458,6 +458,430 @@ </dict> <dict> <key>FooterText</key> + <string> + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. +</string> + <key>License</key> + <string>Apache</string> + <key>Title</key> + <string>GTMSessionFetcher</string> + <key>Type</key> + <string>PSGroupSpecifier</string> + </dict> + <dict> + <key>FooterText</key> + <string> + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. +</string> + <key>License</key> + <string>Apache</string> + <key>Title</key> + <string>GoogleAPIClientForREST</string> + <key>Type</key> + <string>PSGroupSpecifier</string> + </dict> + <dict> + <key>FooterText</key> <string>Copyright 2019 Google</string> <key>License</key> <string>Copyright</string> diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-frameworks.sh b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-frameworks.sh @@ -153,10 +153,14 @@ strip_invalid_archs() { if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework" + install_framework "${BUILT_PRODUCTS_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework" install_framework "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework" install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework" fi if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework" + install_framework "${BUILT_PRODUCTS_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework" install_framework "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework" install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework" fi diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-resources.sh b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-resources.sh @@ -0,0 +1,131 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then + # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy + # resources to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY:-}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_resource "${PODS_ROOT}/GoogleAuthUtilities/Frameworks/frameworks/GoogleAuthUtilities.framework/Resources/GTMOAuth2ViewTouch.xib" + install_resource "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_resource "${PODS_ROOT}/GoogleAuthUtilities/Frameworks/frameworks/GoogleAuthUtilities.framework/Resources/GTMOAuth2ViewTouch.xib" + install_resource "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle" +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + else + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" + fi +fi diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.debug.xcconfig b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.debug.xcconfig @@ -1,8 +1,8 @@ -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "FIRAnalyticsConnector" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" +OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "FIRAnalyticsConnector" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAPIClientForREST" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. diff --git a/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.release.xcconfig b/Pods/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant.release.xcconfig @@ -1,8 +1,8 @@ -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "FIRAnalyticsConnector" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" +OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "FIRAnalyticsConnector" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAPIClientForREST" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. diff --git a/Pods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.debug.xcconfig b/Pods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.debug.xcconfig @@ -1,8 +1,8 @@ -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "Foundation" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" +OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAPIClientForREST" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. diff --git a/Pods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.release.xcconfig b/Pods/Target Support Files/Pods-TeachersAssistantTests/Pods-TeachersAssistantTests.release.xcconfig @@ -1,8 +1,8 @@ -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "Foundation" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" +OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAPIClientForREST" -framework "GoogleUtilities" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "nanopb" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. diff --git a/Teachers' Assistant/AppDelegate.swift b/Teachers' Assistant/AppDelegate.swift @@ -36,8 +36,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UNUserNotificationCenter.current().add(welcomePing, withCompletionHandler: nil) UNUserNotificationCenter.current().delegate = self - /* PERMISSION REQUESTS END */ } + /* PERMISSION REQUESTS END */ return true } diff --git a/Teachers' Assistant/GoogleService-Info.plist b/Teachers' Assistant/GoogleService-Info.plist @@ -23,18 +23,18 @@ <key>STORAGE_BUCKET</key> <string>teachersassistant-1f134.appspot.com</string> <key>IS_ADS_ENABLED</key> - <true></true> + <true/> <key>IS_ANALYTICS_ENABLED</key> - <false></false> + <false/> <key>IS_APPINVITE_ENABLED</key> - <false></false> + <false/> <key>IS_GCM_ENABLED</key> - <true></true> + <true/> <key>IS_SIGNIN_ENABLED</key> - <true></true> + <true/> <key>GOOGLE_APP_ID</key> <string>1:277304690130:ios:6c715d1c3aaa8d4a</string> <key>DATABASE_URL</key> <string>https://teachersassistant-1f134.firebaseio.com</string> </dict> -</plist> -\ No newline at end of file +</plist> diff --git a/Teachers' Assistant/Images/Settings Button.png b/Teachers' Assistant/Images/Settings Button.png Binary files differ. diff --git a/Teachers' Assistant/Info.plist b/Teachers' Assistant/Info.plist @@ -5,7 +5,7 @@ <key>CFBundleDevelopmentRegion</key> <string>$(DEVELOPMENT_LANGUAGE)</string> <key>CFBundleDisplayName</key> - <string>Teachers&apos; Assistant</string> + <string>Teachers' Assistant</string> <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> @@ -18,6 +18,19 @@ <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleURLName</key> + <string></string> + <key>CFBundleURLSchemes</key> + <array> + <string>com.googleusercontent.apps.277304690130-tp39je6dr5d3a6enhdhumh8upc17g68i</string> + </array> + </dict> + </array> <key>CFBundleVersion</key> <string>1</string> <key>LSRequiresIPhoneOS</key> diff --git a/TeachersAssistant.xcodeproj/project.pbxproj b/TeachersAssistant.xcodeproj/project.pbxproj @@ -39,7 +39,6 @@ B33C58C122395CE600E0E75F /* Sign-In (Orange Inv).png in Resources */ = {isa = PBXBuildFile; fileRef = B33C58AF22395CE500E0E75F /* Sign-In (Orange Inv).png */; }; B33C58C222395CE600E0E75F /* Sign-Out (Light Blue Inv).png in Resources */ = {isa = PBXBuildFile; fileRef = B33C58B022395CE600E0E75F /* Sign-Out (Light Blue Inv).png */; }; B34459FD2239A0C400BF6161 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B34459FC2239A0C400BF6161 /* GoogleService-Info.plist */; }; - B34459FE2239A0CD00BF6161 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B34459FC2239A0C400BF6161 /* GoogleService-Info.plist */; }; B34CDB9F221650410031C28B /* Sign-Out (Dark Blue).png in Resources */ = {isa = PBXBuildFile; fileRef = B34CDB8C221650360031C28B /* Sign-Out (Dark Blue).png */; }; B34CDBA0221650410031C28B /* Sign-In (Light Blue).png in Resources */ = {isa = PBXBuildFile; fileRef = B34CDB8D221650360031C28B /* Sign-In (Light Blue).png */; }; B34CDBA1221650410031C28B /* LiveList (Gray).png in Resources */ = {isa = PBXBuildFile; fileRef = B34CDB8E221650370031C28B /* LiveList (Gray).png */; }; @@ -59,7 +58,7 @@ B34CDBB0221650410031C28B /* Sign-Out (Gray).png in Resources */ = {isa = PBXBuildFile; fileRef = B34CDB9D2216503F0031C28B /* Sign-Out (Gray).png */; }; B34CDBB1221650410031C28B /* LiveList (Green).png in Resources */ = {isa = PBXBuildFile; fileRef = B34CDB9E221650400031C28B /* LiveList (Green).png */; }; B3509FC322396C8C007B3024 /* Settings Button Inv.png in Resources */ = {isa = PBXBuildFile; fileRef = B3509FC122396C8B007B3024 /* Settings Button Inv.png */; }; - B3509FC422396C8C007B3024 /* Settings Button.png in Resources */ = {isa = PBXBuildFile; fileRef = B3509FC222396C8B007B3024 /* Settings Button.png */; }; + B360BBBC2239A507006DD2EB /* Settings Button.png in Resources */ = {isa = PBXBuildFile; fileRef = B360BBBB2239A507006DD2EB /* Settings Button.png */; }; B3D8E1D5220F7847007CAD3A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D8E1D4220F7847007CAD3A /* AppDelegate.swift */; }; B3D8E1D7220F7847007CAD3A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D8E1D6220F7847007CAD3A /* ViewController.swift */; }; B3D8E1DA220F7847007CAD3A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3D8E1D8220F7847007CAD3A /* Main.storyboard */; }; @@ -138,7 +137,7 @@ B34CDB9D2216503F0031C28B /* Sign-Out (Gray).png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Sign-Out (Gray).png"; sourceTree = "<group>"; }; B34CDB9E221650400031C28B /* LiveList (Green).png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "LiveList (Green).png"; sourceTree = "<group>"; }; B3509FC122396C8B007B3024 /* Settings Button Inv.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Settings Button Inv.png"; sourceTree = "<group>"; }; - B3509FC222396C8B007B3024 /* Settings Button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Settings Button.png"; path = "../../../../Downloads/settingsDrop/Settings Button.png"; sourceTree = "<group>"; }; + B360BBBB2239A507006DD2EB /* Settings Button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Settings Button.png"; sourceTree = "<group>"; }; B3D8E1D1220F7847007CAD3A /* TeachersAssistant.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TeachersAssistant.app; sourceTree = BUILT_PRODUCTS_DIR; }; B3D8E1D4220F7847007CAD3A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; B3D8E1D6220F7847007CAD3A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; @@ -234,7 +233,7 @@ B34CDB982216503C0031C28B /* Sign-Out (Orange).png */, B34CDB92221650390031C28B /* Sign-Out (Pink).png */, B3509FC122396C8B007B3024 /* Settings Button Inv.png */, - B3509FC222396C8B007B3024 /* Settings Button.png */, + B360BBBB2239A507006DD2EB /* Settings Button.png */, ); path = Images; sourceTree = "<group>"; @@ -427,7 +426,7 @@ B34CDBA1221650410031C28B /* LiveList (Gray).png in Resources */, B32BA0AE2238D6DF0000E747 /* Brandeis_Web_HiRes_Gray.png in Resources */, B33C58B722395CE600E0E75F /* Sign-Out (Gray Inv).png in Resources */, - B3509FC422396C8C007B3024 /* Settings Button.png in Resources */, + B360BBBC2239A507006DD2EB /* Settings Button.png in Resources */, B33C58BE22395CE600E0E75F /* LiveList (Light Blue Inv).png in Resources */, B34CDBA3221650410031C28B /* Sign-Out (Green).png in Resources */, B3D8E1DA220F7847007CAD3A /* Main.storyboard in Resources */, @@ -443,7 +442,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - B34459FE2239A0CD00BF6161 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -457,11 +455,15 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-TeachersAssistant/Pods-TeachersAssistant-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleAPIClientForREST/GoogleAPIClientForREST.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleAPIClientForREST.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); diff --git a/TeachersAssistant.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist b/TeachersAssistant.xcodeproj/xcuserdata/naomi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ <key>TeachersAssistant.xcscheme</key> <dict> <key>orderHint</key> - <integer>9</integer> + <integer>11</integer> </dict> </dict> </dict> diff --git a/TeachersAssistant.xcworkspace/xcuserdata/naomi.xcuserdatad/UserInterfaceState.xcuserstate b/TeachersAssistant.xcworkspace/xcuserdata/naomi.xcuserdatad/UserInterfaceState.xcuserstate Binary files differ.