├── .gitignore ├── AnalyticsSwift iOS-Info.plist ├── AnalyticsSwift-tvOS-Info.plist ├── AnalyticsSwift.podspec ├── AnalyticsSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── AnalyticsSwift-iOS.xcscheme │ ├── AnalyticsSwift-tvOS.xcscheme │ └── AnalyticsSwift.xcscheme ├── AnalyticsSwift ├── AliasMessageBuilder.swift ├── Analytics.swift ├── AnalyticsSwift.h ├── Credentials.swift ├── Executor.swift ├── GroupMessageBuilder.swift ├── IdentifyMessageBuilder.swift ├── Info.plist ├── MessageBuilder.swift ├── ScreenMessageBuilder.swift ├── SerialExecutor.swift └── TrackMessageBuilder.swift ├── AnalyticsSwiftExample ├── AnalyticsSwiftExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── AnalyticsSwiftExample.xcscheme ├── AnalyticsSwiftExample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── AnalyticsSwiftExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── base_small.imageset │ │ │ ├── Contents.json │ │ │ └── base_small.jpg │ │ ├── segment_logo.imageset │ │ │ ├── Contents.json │ │ │ └── segment_logo.png │ │ ├── stack_small.imageset │ │ │ ├── Contents.json │ │ │ └── stack_small.jpg │ │ └── stack_smaller.imageset │ │ │ ├── Contents.json │ │ │ └── stack_smaller.jpg │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── PageViewController.swift │ ├── PrimaryViewController.swift │ └── SecondaryViewController.swift ├── Podfile ├── Podfile.lock └── Pods │ ├── Analytics │ ├── Analytics │ │ ├── Classes │ │ │ ├── Crypto │ │ │ │ ├── SEGAES256Crypto.h │ │ │ │ ├── SEGAES256Crypto.m │ │ │ │ └── SEGCrypto.h │ │ │ ├── Integrations │ │ │ │ ├── SEGAliasPayload.h │ │ │ │ ├── SEGAliasPayload.m │ │ │ │ ├── SEGGroupPayload.h │ │ │ │ ├── SEGGroupPayload.m │ │ │ │ ├── SEGIdentifyPayload.h │ │ │ │ ├── SEGIdentifyPayload.m │ │ │ │ ├── SEGIntegration.h │ │ │ │ ├── SEGIntegrationFactory.h │ │ │ │ ├── SEGIntegrationsManager.h │ │ │ │ ├── SEGIntegrationsManager.m │ │ │ │ ├── SEGPayload.h │ │ │ │ ├── SEGPayload.m │ │ │ │ ├── SEGScreenPayload.h │ │ │ │ ├── SEGScreenPayload.m │ │ │ │ ├── SEGTrackPayload.h │ │ │ │ └── SEGTrackPayload.m │ │ │ ├── Internal │ │ │ │ ├── NSData+SEGGZIP.h │ │ │ │ ├── NSData+SEGGZIP.m │ │ │ │ ├── SEGAnalyticsUtils.h │ │ │ │ ├── SEGAnalyticsUtils.m │ │ │ │ ├── SEGFileStorage.h │ │ │ │ ├── SEGFileStorage.m │ │ │ │ ├── SEGHTTPClient.h │ │ │ │ ├── SEGHTTPClient.m │ │ │ │ ├── SEGSegmentIntegration.h │ │ │ │ ├── SEGSegmentIntegration.m │ │ │ │ ├── SEGSegmentIntegrationFactory.h │ │ │ │ ├── SEGSegmentIntegrationFactory.m │ │ │ │ ├── SEGStorage.h │ │ │ │ ├── SEGStoreKitTracker.h │ │ │ │ ├── SEGStoreKitTracker.m │ │ │ │ ├── SEGUserDefaultsStorage.h │ │ │ │ ├── SEGUserDefaultsStorage.m │ │ │ │ ├── SEGUtils.h │ │ │ │ ├── SEGUtils.m │ │ │ │ ├── UIViewController+SEGScreen.h │ │ │ │ └── UIViewController+SEGScreen.m │ │ │ ├── Middlewares │ │ │ │ ├── SEGContext.h │ │ │ │ ├── SEGContext.m │ │ │ │ ├── SEGMiddleware.h │ │ │ │ └── SEGMiddleware.m │ │ │ ├── SEGAnalytics.h │ │ │ ├── SEGAnalytics.m │ │ │ ├── SEGAnalyticsConfiguration.h │ │ │ ├── SEGAnalyticsConfiguration.m │ │ │ └── SEGSerializableValue.h │ │ └── Vendor │ │ │ ├── SEGReachability.h │ │ │ └── SEGReachability.m │ ├── LICENSE │ └── README.md │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ └── Target Support Files │ ├── Analytics │ ├── Analytics-Info.plist │ ├── Analytics-dummy.m │ ├── Analytics-prefix.pch │ ├── Analytics-umbrella.h │ ├── Analytics.modulemap │ └── Analytics.xcconfig │ └── Pods-AnalyticsSwiftExample │ ├── Pods-AnalyticsSwiftExample-Info.plist │ ├── Pods-AnalyticsSwiftExample-acknowledgements.markdown │ ├── Pods-AnalyticsSwiftExample-acknowledgements.plist │ ├── Pods-AnalyticsSwiftExample-dummy.m │ ├── Pods-AnalyticsSwiftExample-frameworks.sh │ ├── Pods-AnalyticsSwiftExample-umbrella.h │ ├── Pods-AnalyticsSwiftExample.debug.xcconfig │ ├── Pods-AnalyticsSwiftExample.modulemap │ └── Pods-AnalyticsSwiftExample.release.xcconfig ├── AnalyticsSwiftTests ├── AnalyticsSwiftTests.swift ├── Info.plist └── SynchronousExecutor.swift ├── LICENSE.md ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Objective-C ### 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | #Pods/ 30 | 31 | 32 | ### Swift ### 33 | # Xcode 34 | # 35 | build/ 36 | *.pbxuser 37 | !default.pbxuser 38 | *.mode1v3 39 | !default.mode1v3 40 | *.mode2v3 41 | !default.mode2v3 42 | *.perspectivev3 43 | !default.perspectivev3 44 | xcuserdata 45 | *.xccheckout 46 | *.moved-aside 47 | DerivedData 48 | *.hmap 49 | *.ipa 50 | *.xcuserstate 51 | 52 | # CocoaPods 53 | # 54 | # We recommend against adding the Pods directory to your .gitignore. However 55 | # you should judge for yourself, the pros and cons are mentioned at: 56 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 57 | # 58 | # Pods/ 59 | 60 | # Carthage 61 | # 62 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 63 | # Carthage/Checkouts 64 | 65 | Carthage/Build 66 | 67 | 68 | ### OSX ### 69 | .DS_Store 70 | .AppleDouble 71 | .LSOverride 72 | 73 | # Icon must end with two \r 74 | Icon 75 | 76 | # Thumbnails 77 | ._* 78 | 79 | # Files that might appear in the root of a volume 80 | .DocumentRevisions-V100 81 | .fseventsd 82 | .Spotlight-V100 83 | .TemporaryItems 84 | .Trashes 85 | .VolumeIcon.icns 86 | 87 | # Directories potentially created on remote AFP share 88 | .AppleDB 89 | .AppleDesktop 90 | Network Trash Folder 91 | Temporary Items 92 | .apdisk 93 | 94 | -------------------------------------------------------------------------------- /AnalyticsSwift iOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Segment. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /AnalyticsSwift-tvOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Segment. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /AnalyticsSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'AnalyticsSwift' 3 | spec.version = '0.2.0' 4 | 5 | spec.license = { :type => 'MIT' } 6 | 7 | spec.homepage = 'https://github.com/segmentio/analytics-swift' 8 | spec.authors = { 'Segment' => 'friends@segment.com' } 9 | spec.summary = 'The hassle-free way to add analytics to your Swift app.' 10 | 11 | spec.source = { :git => 'https://github.com/segmentio/analytics-swift.git', :tag => spec.version } 12 | 13 | spec.source_files = 'AnalyticsSwift/*.swift' 14 | 15 | spec.ios.deployment_target = '8.0' 16 | spec.osx.deployment_target = '10.9' 17 | spec.tvos.deployment_target = '9.0' 18 | 19 | spec.requires_arc = true 20 | end 21 | -------------------------------------------------------------------------------- /AnalyticsSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AnalyticsSwift.xcodeproj/xcshareddata/xcschemes/AnalyticsSwift-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /AnalyticsSwift.xcodeproj/xcshareddata/xcschemes/AnalyticsSwift-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /AnalyticsSwift.xcodeproj/xcshareddata/xcschemes/AnalyticsSwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /AnalyticsSwift/AliasMessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public class AliasMessageBuilder: MessageBuilder { 26 | private var dictionary: [String: AnyObject] = [:] 27 | 28 | public init(previousId: String) { 29 | dictionary["type"] = "alias" as AnyObject 30 | dictionary["previousId"] = previousId as AnyObject 31 | } 32 | 33 | // Common 34 | 35 | public func userId(_ userId: String) -> AliasMessageBuilder { 36 | dictionary["userId"] = userId as AnyObject 37 | return self 38 | } 39 | 40 | public func anonymousId(_ anonymousId: String) -> AliasMessageBuilder { 41 | dictionary["anonymousId"] = anonymousId as AnyObject 42 | return self 43 | } 44 | 45 | public func context(_ context: [String: AnyObject]) -> AliasMessageBuilder { 46 | dictionary["context"] = context as AnyObject 47 | return self 48 | } 49 | 50 | public func build() -> [String: AnyObject] { 51 | return dictionary 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /AnalyticsSwift/Analytics.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | The entry point into the Analytics for Swift library. 27 | */ 28 | 29 | public class Analytics { 30 | 31 | /** 32 | The writeKey for the Segment project this client will upload events to. 33 | */ 34 | 35 | private let writeKey: String 36 | 37 | /** 38 | In memory queue of enqueued events. 39 | */ 40 | 41 | private var messageQueue: [[String: AnyObject]] 42 | 43 | /** 44 | Executor that handles interactions with the messageQueue. This will also be used to upload events. 45 | */ 46 | 47 | private let executor: Executor 48 | 49 | /** 50 | Queue size at which we automatically upload events 51 | */ 52 | 53 | private let flushQueueSize = 10 54 | 55 | // MARK: Public interface 56 | 57 | /** 58 | Initializes a new Analytics client with the provided parameters. 59 | - parameter writeKey: Write Key for your Segment project 60 | - returns: A beautiful, brand-new, custom built Analytics client just for you. ❤️ 61 | */ 62 | 63 | public static func create(writeKey: String) -> Analytics { 64 | let executor = SerialExecutor(name:"com.segment.executor." + writeKey) 65 | return Analytics(writeKey: writeKey, queue: [], executor: executor) 66 | } 67 | 68 | /** 69 | Initializes a new Analytics client with the provided parameters. 70 | - parameter writeKey: Write Key for your Segment project 71 | - parameter queue: In memory queue of enqueued events. 72 | - parameter executor: Executor that handles interactions with the messageQueue. This will also be used to upload events. 73 | Exposed for testing only. Do not use this directly. 74 | */ 75 | 76 | public init(writeKey: String, queue: [[String: AnyObject]], executor: Executor) { 77 | self.writeKey = writeKey 78 | self.messageQueue = queue 79 | self.executor = executor 80 | } 81 | 82 | /** 83 | Enqueue the given message to be uploaded to Segment asynchronously. 84 | This function will call MessageBuilder.build and validate the message. 85 | - parameter messageBuilder: The builder instance used to a create a message. Be sure to provide a userId or anonymousId. 86 | */ 87 | 88 | public func enqueue(messageBuilder: MessageBuilder) { 89 | var message = messageBuilder.build() 90 | Analytics.ensureId(message: message) 91 | message["messageId"] = UUID().uuidString as AnyObject 92 | message["timestamp"] = now as AnyObject 93 | executor.submit { 94 | self.messageQueue.append(message) 95 | if self.messageQueue.count >= self.flushQueueSize { 96 | self.performFlush() 97 | } 98 | } 99 | } 100 | 101 | /** 102 | Request the client to upload events. This method will asychronously upload the events, and will not block. 103 | */ 104 | 105 | public func flush() { 106 | executor.submit(task: performFlush) 107 | } 108 | 109 | // MARK: Private methods 110 | 111 | /** 112 | Ensures that a message provides either one of userId or anonymousId. 113 | */ 114 | 115 | private static func ensureId(message: [String: AnyObject]) { 116 | let idExists = message.keys.contains("userId") || message.keys.contains("anonymousId") 117 | assert(idExists, "Either userId or anonymousId must be provided.") 118 | } 119 | 120 | /** 121 | Returns a URLRequest for the given endpoint (relative to https://api.segment.io/v1 by default). 122 | */ 123 | 124 | private func request(endpoint: String) -> URLRequest { 125 | return URLRequest(url: URL(string: "https://api.segment.io/v1" + endpoint)!) 126 | } 127 | 128 | /** 129 | Returns the current time as an ISO 8601 formatted String. 130 | */ 131 | 132 | private var now: String { 133 | let formatter = DateFormatter() 134 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z" 135 | formatter.timeZone = TimeZone(secondsFromGMT: 0) 136 | formatter.calendar = Calendar(identifier: .iso8601) 137 | formatter.locale = Locale(identifier: "en_US_POSIX") 138 | return formatter.string(from: Date()) 139 | } 140 | 141 | /** 142 | Synchronously flush events to Segment. 143 | */ 144 | 145 | private func performFlush() { 146 | guard messageQueue.count > 0 else { 147 | print("No messages to flush") 148 | return 149 | } 150 | guard let messagesData = messagesData else { 151 | print("Failed to serialize messages. Dropping \(messageQueue.count) messages.") 152 | messageQueue.removeAll(keepingCapacity: true) 153 | return 154 | } 155 | let request = urlRequest(for: messagesData) 156 | print("Uploading \(messageQueue.count) messages.") 157 | let dataTask = URLSession.shared.dataTask(with: request) 158 | dataTask.resume() 159 | messageQueue.removeAll(keepingCapacity: true) 160 | } 161 | 162 | /** 163 | Returns serialized events. 164 | */ 165 | 166 | private var messagesData: Data? { 167 | let batch: [String: AnyObject] = [ 168 | "batch": messageQueue as AnyObject, 169 | "context": [ 170 | "library": [ 171 | "name": "analytics-swift", 172 | "version": AnalyticsSwiftVersionNumber 173 | ] 174 | ] as AnyObject 175 | ] 176 | return try? JSONSerialization.data(withJSONObject: batch, options: []) 177 | } 178 | 179 | /** 180 | Returns URLRequest to be fired to send events to Segment. 181 | */ 182 | 183 | private func urlRequest(for messagesData: Data) -> URLRequest { 184 | var urlRequest = request(endpoint: "/import") 185 | urlRequest.httpMethod = "post" 186 | urlRequest.setValue("gzip", forHTTPHeaderField: "Accept-Encoding") 187 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 188 | urlRequest.setValue(Credentials.basic(username: writeKey, password: ""), forHTTPHeaderField: "Authorization") 189 | urlRequest.httpBody = messagesData 190 | return urlRequest 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /AnalyticsSwift/AnalyticsSwift.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsSwift.h 3 | // AnalyticsSwift 4 | // 5 | // Created by Prateek Srivastava on 2015-06-10. 6 | // Copyright (c) 2015 Segment. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE || TARGET_OS_TVOS 12 | 13 | #import 14 | 15 | 16 | #else 17 | 18 | #import 19 | 20 | 21 | #endif 22 | 23 | 24 | //! Project version number for AnalyticsSwift. 25 | FOUNDATION_EXPORT double AnalyticsSwiftVersionNumber; 26 | 27 | //! Project version string for AnalyticsSwift. 28 | FOUNDATION_EXPORT const unsigned char AnalyticsSwiftVersionString[]; 29 | 30 | // In this header, you should import all the public headers of your framework using statements like #import 31 | 32 | 33 | -------------------------------------------------------------------------------- /AnalyticsSwift/Credentials.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | Factory for HTTP authorization credentials. Exposed for testing. 27 | */ 28 | 29 | public class Credentials { 30 | 31 | /** 32 | Returns an auth credential for the Basic scheme. Exposed for testing. 33 | */ 34 | 35 | public static func basic(username: String, password: String) -> String { 36 | return String(format: "Basic %@", String(format: "%@:%@", username, password).base64) 37 | } 38 | } 39 | 40 | extension String { 41 | 42 | /** 43 | Returns Base64 encoded string. 44 | */ 45 | 46 | var base64: String { 47 | let utf8str = data(using: .utf8, allowLossyConversion: false) 48 | return utf8str?.base64EncodedString(options: .lineLength64Characters) ?? "" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /AnalyticsSwift/Executor.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | Abstraction for enqueing tasks. This interface provides a way of decoupling task submission 27 | from the mechanics of how each task will be run, including details of thread use, scheduling, etc. 28 | Exposed for testing. 29 | */ 30 | 31 | public protocol Executor { 32 | func submit(task: @escaping () -> ()) 33 | } 34 | -------------------------------------------------------------------------------- /AnalyticsSwift/GroupMessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public class GroupMessageBuilder: MessageBuilder { 26 | private var dictionary: [String: AnyObject] = [:] 27 | 28 | public init(groupId: String) { 29 | dictionary["type"] = "group" as AnyObject 30 | dictionary["groupId"] = groupId as AnyObject 31 | } 32 | 33 | public func traits(_ traits: [String: AnyObject]) -> GroupMessageBuilder { 34 | dictionary["traits"] = traits as AnyObject 35 | return self 36 | } 37 | 38 | // Common 39 | 40 | public func userId(_ userId: String) -> GroupMessageBuilder { 41 | dictionary["userId"] = userId as AnyObject 42 | return self 43 | } 44 | 45 | public func anonymousId(_ anonymousId: String) -> GroupMessageBuilder { 46 | dictionary["anonymousId"] = anonymousId as AnyObject 47 | return self 48 | } 49 | 50 | public func context(_ context: [String: AnyObject]) -> GroupMessageBuilder { 51 | dictionary["context"] = context as AnyObject 52 | return self 53 | } 54 | 55 | public func build() -> [String: AnyObject] { 56 | return dictionary 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AnalyticsSwift/IdentifyMessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public class IdentifyMessageBuilder: MessageBuilder { 26 | private var dictionary: [String: AnyObject] = [:] 27 | 28 | public init() { 29 | dictionary["type"] = "identify" as AnyObject 30 | } 31 | 32 | public func traits(_ traits: [String: AnyObject]) -> IdentifyMessageBuilder { 33 | dictionary["traits"] = traits as AnyObject 34 | return self 35 | } 36 | 37 | // Common 38 | 39 | public func userId(_ userId: String) -> IdentifyMessageBuilder { 40 | dictionary["userId"] = userId as AnyObject 41 | return self 42 | } 43 | 44 | public func anonymousId(_ anonymousId: String) -> IdentifyMessageBuilder { 45 | dictionary["anonymousId"] = anonymousId as AnyObject 46 | return self 47 | } 48 | 49 | public func context(_ context: [String: AnyObject]) -> IdentifyMessageBuilder { 50 | dictionary["context"] = context as AnyObject 51 | return self 52 | } 53 | 54 | public func build() -> [String: AnyObject] { 55 | return dictionary 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /AnalyticsSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Segment. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /AnalyticsSwift/MessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public protocol MessageBuilder { 26 | func build() -> [String: AnyObject] 27 | } 28 | -------------------------------------------------------------------------------- /AnalyticsSwift/ScreenMessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public class ScreenMessageBuilder: MessageBuilder { 26 | private var dictionary: [String: AnyObject] = [:] 27 | 28 | public init(name: String) { 29 | dictionary["type"] = "screen" as AnyObject 30 | dictionary["name"] = name as AnyObject 31 | } 32 | 33 | public func properties(_ properties: [String: AnyObject]) -> ScreenMessageBuilder { 34 | dictionary["properties"] = properties as AnyObject 35 | return self 36 | } 37 | 38 | // Common 39 | 40 | public func userId(_ userId: String) -> ScreenMessageBuilder { 41 | dictionary["userId"] = userId as AnyObject 42 | return self 43 | } 44 | 45 | public func anonymousId(_ anonymousId: String) -> ScreenMessageBuilder { 46 | dictionary["anonymousId"] = anonymousId as AnyObject 47 | return self 48 | } 49 | 50 | public func context(_ context: [String: AnyObject]) -> ScreenMessageBuilder { 51 | dictionary["context"] = context as AnyObject 52 | return self 53 | } 54 | 55 | public func build() -> [String: AnyObject] { 56 | return dictionary 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AnalyticsSwift/SerialExecutor.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | An Executor implementation that runs all enqueued tasks serially and asynchronously. 27 | */ 28 | 29 | public class SerialExecutor: Executor { 30 | private let dispatcher: DispatchQueue 31 | 32 | init(name: String) { 33 | self.dispatcher = DispatchQueue(label: name) 34 | } 35 | 36 | public func submit(task: @escaping () -> ()) { 37 | dispatcher.async(execute: task) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /AnalyticsSwift/TrackMessageBuilder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | 25 | public class TrackMessageBuilder: MessageBuilder { 26 | private var dictionary: [String: AnyObject] = [:] 27 | 28 | public init(event: String) { 29 | dictionary["type"] = "track" as AnyObject 30 | dictionary["event"] = event as AnyObject 31 | } 32 | 33 | public func properties(properties: [String: AnyObject]) -> TrackMessageBuilder { 34 | dictionary["properties"] = properties as AnyObject 35 | return self 36 | } 37 | 38 | // Common 39 | 40 | public func userId(_ userId: String) -> TrackMessageBuilder { 41 | dictionary["userId"] = userId as AnyObject 42 | return self 43 | } 44 | 45 | public func anonymousId(_ anonymousId: String) -> TrackMessageBuilder { 46 | dictionary["anonymousId"] = anonymousId as AnyObject 47 | return self 48 | } 49 | 50 | public func context(_ context: [String: AnyObject]) -> TrackMessageBuilder { 51 | dictionary["context"] = context as AnyObject 52 | return self 53 | } 54 | 55 | public func build() -> [String: AnyObject] { 56 | return dictionary 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample.xcodeproj/xcshareddata/xcschemes/AnalyticsSwiftExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AnalyticsSwiftExample 4 | // 5 | // Created by William Grosset on 2/12/19. 6 | // Copyright © 2019 Segment. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Analytics 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | let config = SEGAnalyticsConfiguration(writeKey: "YOUR_WRITE_KEY") 19 | config.trackApplicationLifecycleEvents = true 20 | SEGAnalytics.setup(with: config) 21 | return true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/base_small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "base_small.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/base_small.imageset/base_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segment-boneyard/analytics-swift/f96417b55e3ba99891d59ef60671feb9ca9ae6df/AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/base_small.imageset/base_small.jpg -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/segment_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "segment_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/segment_logo.imageset/segment_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segment-boneyard/analytics-swift/f96417b55e3ba99891d59ef60671feb9ca9ae6df/AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/segment_logo.imageset/segment_logo.png -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "stack_small.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_small.imageset/stack_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segment-boneyard/analytics-swift/f96417b55e3ba99891d59ef60671feb9ca9ae6df/AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_small.imageset/stack_small.jpg -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_smaller.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "stack_smaller.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_smaller.imageset/stack_smaller.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segment-boneyard/analytics-swift/f96417b55e3ba99891d59ef60671feb9ca9ae6df/AnalyticsSwiftExample/AnalyticsSwiftExample/Assets.xcassets/stack_smaller.imageset/stack_smaller.jpg -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 | 79 | 80 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/PageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.swift 3 | // AnalyticsSwiftExample 4 | // 5 | // Created by William Grosset on 2/13/19. 6 | // Copyright © 2019 Segment. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource { 12 | 13 | lazy var orderedViewControllers: [UIViewController] = { 14 | return [self.newViewController(viewController: "sbPrimary"), 15 | self.newViewController(viewController: "sbSecondary")] 16 | }() 17 | 18 | var pageControl = UIPageControl() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | self.delegate = self 24 | self.dataSource = self 25 | 26 | if let firstViewController = orderedViewControllers.first { 27 | setViewControllers([firstViewController], 28 | direction: .forward, 29 | animated: true, 30 | completion: nil) 31 | } 32 | configurePageControl() 33 | } 34 | 35 | func configurePageControl() { 36 | pageControl = UIPageControl(frame: CGRect(x: 0, y: UIScreen.main.bounds.maxY - 50, width: UIScreen.main.bounds.width, height: 50)) 37 | pageControl.numberOfPages = orderedViewControllers.count 38 | pageControl.currentPage = 0 39 | pageControl.tintColor = UIColor.black 40 | pageControl.pageIndicatorTintColor = UIColor.black 41 | pageControl.currentPageIndicatorTintColor = UIColor(red: 73/255, green: 184/255, blue: 130/255, alpha: 1) 42 | self.view.addSubview(pageControl) 43 | } 44 | 45 | func newViewController(viewController: String) -> UIViewController { 46 | return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController) 47 | } 48 | 49 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 50 | guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 51 | return nil 52 | } 53 | 54 | let previousIndex = viewControllerIndex - 1 55 | 56 | guard previousIndex >= 0 else { 57 | return nil 58 | } 59 | 60 | guard orderedViewControllers.count > previousIndex else { 61 | return nil 62 | } 63 | 64 | return orderedViewControllers[previousIndex] 65 | } 66 | 67 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 68 | guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 69 | return nil 70 | } 71 | 72 | let nextIndex = viewControllerIndex + 1 73 | 74 | guard orderedViewControllers.count != nextIndex else { 75 | return nil 76 | } 77 | 78 | guard orderedViewControllers.count > nextIndex else { 79 | return nil 80 | } 81 | 82 | return orderedViewControllers[nextIndex] 83 | } 84 | 85 | func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 86 | let pageContentViewController = pageViewController.viewControllers![0] 87 | self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)! 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/PrimaryViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrimaryViewController.swift 3 | // AnalyticsSwiftExample 4 | // 5 | // Created by William Grosset on 2/12/19. 6 | // Copyright © 2019 Segment. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Analytics 11 | 12 | class PrimaryViewController: UIViewController { 13 | 14 | override func viewDidAppear(_ animated: Bool) { 15 | super.viewDidAppear(animated) 16 | SEGAnalytics.shared().screen("Home") 17 | SEGAnalytics.shared().flush() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/AnalyticsSwiftExample/SecondaryViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondaryViewController.swift 3 | // AnalyticsSwiftExample 4 | // 5 | // Created by William Grosset on 2/12/19. 6 | // Copyright © 2019 Segment. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Analytics 11 | 12 | class SecondaryViewController: UIViewController { 13 | 14 | @IBOutlet weak var button: UIButton! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | button.layer.cornerRadius = 10 19 | button.layer.shadowColor = UIColor.black.cgColor 20 | button.layer.shadowOffset = CGSize(width: 0, height: 5) 21 | button.layer.shadowRadius = 5 22 | button.layer.shadowOpacity = 0.4 23 | button.titleLabel?.textAlignment = NSTextAlignment.center 24 | } 25 | 26 | override func viewDidAppear(_ animated: Bool) { 27 | super.viewDidAppear(animated) 28 | SEGAnalytics.shared().screen("About") 29 | SEGAnalytics.shared().flush() 30 | } 31 | 32 | @IBAction func buttonClicked(_ sender: Any) { 33 | SEGAnalytics.shared().track("Learn About Segment Clicked") 34 | guard let url = URL(string: "https://github.com/segmentio/analytics-ios/blob/master/README.md#quickstart") else { return } 35 | UIApplication.shared.open(url) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | 3 | target 'AnalyticsSwiftExample' do 4 | use_frameworks! 5 | 6 | pod 'Analytics', '3.6.10' 7 | end 8 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Analytics (3.6.10) 3 | 4 | DEPENDENCIES: 5 | - Analytics (= 3.6.10) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - Analytics 10 | 11 | SPEC CHECKSUMS: 12 | Analytics: 63744ad4afa65c3bcdcdb7a94b62515bde5b3900 13 | 14 | PODFILE CHECKSUM: 1078e7b12a3b5e456d114d57af4de4aca03fb029 15 | 16 | COCOAPODS: 1.6.0 17 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Crypto/SEGAES256Crypto.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGAES256Crypto.h 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "SEGCrypto.h" 10 | 11 | 12 | @interface SEGAES256Crypto : NSObject 13 | 14 | @property (nonatomic, readonly, nonnull) NSString *password; 15 | @property (nonatomic, readonly, nonnull) NSData *salt; 16 | @property (nonatomic, readonly, nonnull) NSData *iv; 17 | 18 | - (instancetype _Nonnull)initWithPassword:(NSString *_Nonnull)password salt:(NSData *_Nonnull)salt iv:(NSData *_Nonnull)iv; 19 | // Convenient shorthand. Will randomly generate salt and iv. 20 | - (instancetype _Nonnull)initWithPassword:(NSString *_Nonnull)password; 21 | 22 | + (NSData *_Nonnull)randomDataOfLength:(size_t)length; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Crypto/SEGAES256Crypto.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGAES256Crypto.m 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | 9 | #import 10 | #import 11 | #import "SEGAES256Crypto.h" 12 | #import "SEGUtils.h" 13 | 14 | // Implementation courtesy of http://robnapier.net/aes-commoncrypto 15 | 16 | static NSString *const kRNCryptManagerErrorDomain = @"com.segment.crypto"; 17 | 18 | static const CCAlgorithm kAlgorithm = kCCAlgorithmAES; 19 | static const NSUInteger kAlgorithmKeySize = kCCKeySizeAES256; 20 | static const NSUInteger kAlgorithmBlockSize = kCCBlockSizeAES128; 21 | static const NSUInteger kAlgorithmIVSize = kCCBlockSizeAES128; 22 | static const NSUInteger kPBKDFSaltSize = 8; 23 | static const NSUInteger kPBKDFRounds = 10000; // ~80ms on an iPhone 4 24 | 25 | 26 | @implementation SEGAES256Crypto 27 | 28 | - (instancetype)initWithPassword:(NSString *)password salt:(NSData *)salt iv:(NSData *_Nonnull)iv 29 | { 30 | if (self = [super init]) { 31 | _password = password; 32 | _salt = salt; 33 | _iv = iv; 34 | } 35 | return self; 36 | } 37 | 38 | - (instancetype)initWithPassword:(NSString *)password 39 | { 40 | NSData *iv = [SEGAES256Crypto randomDataOfLength:kAlgorithmIVSize]; 41 | NSData *salt = [SEGAES256Crypto randomDataOfLength:kPBKDFSaltSize]; 42 | return [self initWithPassword:password salt:salt iv:iv]; 43 | } 44 | 45 | - (NSData *)aesKey 46 | { 47 | return [[self class] AESKeyForPassword:self.password salt:self.salt]; 48 | } 49 | 50 | - (NSData *)encrypt:(NSData *)data 51 | { 52 | size_t outLength; 53 | NSMutableData *cipherData = [NSMutableData dataWithLength:data.length + kAlgorithmBlockSize]; 54 | 55 | CCCryptorStatus 56 | result = CCCrypt(kCCEncrypt, // operation 57 | kAlgorithm, // Algorithm 58 | kCCOptionPKCS7Padding, // options 59 | self.aesKey.bytes, // key 60 | self.aesKey.length, // keylength 61 | self.iv.bytes, // iv 62 | data.bytes, // dataIn 63 | data.length, // dataInLength, 64 | cipherData.mutableBytes, // dataOut 65 | cipherData.length, // dataOutAvailable 66 | &outLength); // dataOutMoved 67 | 68 | if (result == kCCSuccess) { 69 | cipherData.length = outLength; 70 | } else { 71 | NSError *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain 72 | code:result 73 | userInfo:nil]; 74 | SEGLog(@"Unable to encrypt data", error); 75 | return nil; 76 | } 77 | return cipherData; 78 | } 79 | 80 | - (NSData *)decrypt:(NSData *)data 81 | { 82 | size_t outLength; 83 | NSMutableData *decryptedData = [NSMutableData dataWithLength:data.length + kAlgorithmBlockSize]; 84 | 85 | CCCryptorStatus 86 | result = CCCrypt(kCCDecrypt, // operation 87 | kAlgorithm, // Algorithm 88 | kCCOptionPKCS7Padding, // options 89 | self.aesKey.bytes, // key 90 | self.aesKey.length, // keylength 91 | self.iv.bytes, // iv 92 | data.bytes, // dataIn 93 | data.length, // dataInLength, 94 | decryptedData.mutableBytes, // dataOut 95 | decryptedData.length, // dataOutAvailable 96 | &outLength); // dataOutMoved 97 | 98 | if (result == kCCSuccess) { 99 | decryptedData.length = outLength; 100 | } else { 101 | NSError *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain 102 | code:result 103 | userInfo:nil]; 104 | SEGLog(@"Unable to decrypt data", error); 105 | return nil; 106 | } 107 | return decryptedData; 108 | } 109 | 110 | + (NSData *)randomDataOfLength:(size_t)length 111 | { 112 | NSMutableData *data = [NSMutableData dataWithLength:length]; 113 | 114 | int result = SecRandomCopyBytes(kSecRandomDefault, 115 | length, 116 | data.mutableBytes); 117 | if (result != kCCSuccess) { 118 | SEGLog(@"Unable to generate random bytes: %d", result); 119 | } 120 | 121 | return data; 122 | } 123 | 124 | // Replace this with a 10,000 hash calls if you don't have CCKeyDerivationPBKDF 125 | + (NSData *)AESKeyForPassword:(NSString *)password 126 | salt:(NSData *)salt 127 | { 128 | NSMutableData *derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize]; 129 | 130 | int result = CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm 131 | password.UTF8String, // password 132 | [password lengthOfBytesUsingEncoding:NSUTF8StringEncoding], // passwordLength 133 | salt.bytes, // salt 134 | salt.length, // saltLen 135 | kCCPRFHmacAlgSHA1, // PRF 136 | kPBKDFRounds, // rounds 137 | derivedKey.mutableBytes, // derivedKey 138 | derivedKey.length); // derivedKeyLen 139 | 140 | // Do not log password here 141 | if (result != kCCSuccess) { 142 | SEGLog(@"Unable to create AES key for password: %d", result); 143 | } 144 | 145 | return derivedKey; 146 | } 147 | 148 | @end 149 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Crypto/SEGCrypto.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGCrypto.h 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @protocol SEGCrypto 11 | 12 | - (NSData *_Nullable)encrypt:(NSData *_Nonnull)data; 13 | - (NSData *_Nullable)decrypt:(NSData *_Nonnull)data; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGAliasPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGPayload.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGAliasPayload : SEGPayload 8 | 9 | @property (nonatomic, readonly) NSString *theNewId; 10 | 11 | - (instancetype)initWithNewId:(NSString *)newId 12 | context:(JSON_DICT)context 13 | integrations:(JSON_DICT)integrations; 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGAliasPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGAliasPayload.h" 2 | 3 | 4 | @implementation SEGAliasPayload 5 | 6 | - (instancetype)initWithNewId:(NSString *)newId 7 | context:(NSDictionary *)context 8 | integrations:(NSDictionary *)integrations 9 | { 10 | if (self = [super initWithContext:context integrations:integrations]) { 11 | _theNewId = [newId copy]; 12 | } 13 | return self; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGGroupPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGPayload.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGGroupPayload : SEGPayload 8 | 9 | @property (nonatomic, readonly) NSString *groupId; 10 | 11 | @property (nonatomic, readonly, nullable) JSON_DICT traits; 12 | 13 | - (instancetype)initWithGroupId:(NSString *)groupId 14 | traits:(JSON_DICT _Nullable)traits 15 | context:(JSON_DICT)context 16 | integrations:(JSON_DICT)integrations; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGGroupPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGGroupPayload.h" 2 | 3 | 4 | @implementation SEGGroupPayload 5 | 6 | - (instancetype)initWithGroupId:(NSString *)groupId 7 | traits:(NSDictionary *)traits 8 | context:(NSDictionary *)context 9 | integrations:(NSDictionary *)integrations 10 | { 11 | if (self = [super initWithContext:context integrations:integrations]) { 12 | _groupId = [groupId copy]; 13 | _traits = [traits copy]; 14 | } 15 | return self; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGIdentifyPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGPayload.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGIdentifyPayload : SEGPayload 8 | 9 | @property (nonatomic, readonly, nullable) NSString *userId; 10 | 11 | @property (nonatomic, readonly, nullable) NSString *anonymousId; 12 | 13 | @property (nonatomic, readonly, nullable) JSON_DICT traits; 14 | 15 | - (instancetype)initWithUserId:(NSString *)userId 16 | anonymousId:(NSString *_Nullable)anonymousId 17 | traits:(JSON_DICT _Nullable)traits 18 | context:(JSON_DICT)context 19 | integrations:(JSON_DICT)integrations; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGIdentifyPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGIdentifyPayload.h" 2 | 3 | 4 | @implementation SEGIdentifyPayload 5 | 6 | - (instancetype)initWithUserId:(NSString *)userId 7 | anonymousId:(NSString *)anonymousId 8 | traits:(NSDictionary *)traits 9 | context:(NSDictionary *)context 10 | integrations:(NSDictionary *)integrations 11 | { 12 | if (self = [super initWithContext:context integrations:integrations]) { 13 | _userId = [userId copy]; 14 | _anonymousId = [anonymousId copy]; 15 | _traits = [traits copy]; 16 | } 17 | return self; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGIntegration.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGIdentifyPayload.h" 3 | #import "SEGTrackPayload.h" 4 | #import "SEGScreenPayload.h" 5 | #import "SEGAliasPayload.h" 6 | #import "SEGIdentifyPayload.h" 7 | #import "SEGGroupPayload.h" 8 | #import "SEGContext.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @protocol SEGIntegration 13 | 14 | @optional 15 | // Identify will be called when the user calls either of the following: 16 | // 1. [[SEGAnalytics sharedInstance] identify:someUserId]; 17 | // 2. [[SEGAnalytics sharedInstance] identify:someUserId traits:someTraits]; 18 | // 3. [[SEGAnalytics sharedInstance] identify:someUserId traits:someTraits options:someOptions]; 19 | // @see https://segment.com/docs/spec/identify/ 20 | - (void)identify:(SEGIdentifyPayload *)payload; 21 | 22 | // Track will be called when the user calls either of the following: 23 | // 1. [[SEGAnalytics sharedInstance] track:someEvent]; 24 | // 2. [[SEGAnalytics sharedInstance] track:someEvent properties:someProperties]; 25 | // 3. [[SEGAnalytics sharedInstance] track:someEvent properties:someProperties options:someOptions]; 26 | // @see https://segment.com/docs/spec/track/ 27 | - (void)track:(SEGTrackPayload *)payload; 28 | 29 | // Screen will be called when the user calls either of the following: 30 | // 1. [[SEGAnalytics sharedInstance] screen:someEvent]; 31 | // 2. [[SEGAnalytics sharedInstance] screen:someEvent properties:someProperties]; 32 | // 3. [[SEGAnalytics sharedInstance] screen:someEvent properties:someProperties options:someOptions]; 33 | // @see https://segment.com/docs/spec/screen/ 34 | - (void)screen:(SEGScreenPayload *)payload; 35 | 36 | // Group will be called when the user calls either of the following: 37 | // 1. [[SEGAnalytics sharedInstance] group:someGroupId]; 38 | // 2. [[SEGAnalytics sharedInstance] group:someGroupId traits:]; 39 | // 3. [[SEGAnalytics sharedInstance] group:someGroupId traits:someGroupTraits options:someOptions]; 40 | // @see https://segment.com/docs/spec/group/ 41 | - (void)group:(SEGGroupPayload *)payload; 42 | 43 | // Alias will be called when the user calls either of the following: 44 | // 1. [[SEGAnalytics sharedInstance] alias:someNewId]; 45 | // 2. [[SEGAnalytics sharedInstance] alias:someNewId options:someOptions]; 46 | // @see https://segment.com/docs/spec/alias/ 47 | - (void)alias:(SEGAliasPayload *)payload; 48 | 49 | // Reset is invoked when the user logs out, and any data saved about the user should be cleared. 50 | - (void)reset; 51 | 52 | // Flush is invoked when any queued events should be uploaded. 53 | - (void)flush; 54 | 55 | // App Delegate Callbacks 56 | 57 | // Callbacks for notifications changes. 58 | // ------------------------------------ 59 | - (void)receivedRemoteNotification:(NSDictionary *)userInfo; 60 | - (void)failedToRegisterForRemoteNotificationsWithError:(NSError *)error; 61 | - (void)registeredForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; 62 | - (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo; 63 | 64 | // Callbacks for app state changes 65 | // ------------------------------- 66 | 67 | - (void)applicationDidFinishLaunching:(NSNotification *)notification; 68 | - (void)applicationDidEnterBackground; 69 | - (void)applicationWillEnterForeground; 70 | - (void)applicationWillTerminate; 71 | - (void)applicationWillResignActive; 72 | - (void)applicationDidBecomeActive; 73 | 74 | - (void)continueUserActivity:(NSUserActivity *)activity; 75 | - (void)openURL:(NSURL *)url options:(NSDictionary *)options; 76 | 77 | @end 78 | 79 | NS_ASSUME_NONNULL_END 80 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGIntegrationFactory.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGIntegration.h" 3 | #import "SEGAnalytics.h" 4 | 5 | NS_ASSUME_NONNULL_BEGIN 6 | 7 | @class SEGAnalytics; 8 | 9 | @protocol SEGIntegrationFactory 10 | 11 | /** 12 | * Attempts to create an adapter with the given settings. Returns the adapter if one was created, or null 13 | * if this factory isn't capable of creating such an adapter. 14 | */ 15 | - (id)createWithSettings:(NSDictionary *)settings forAnalytics:(SEGAnalytics *)analytics; 16 | 17 | /** The key for which this factory can create an Integration. */ 18 | - (NSString *)key; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGIntegrationsManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGIntegrationsManager.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/20/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SEGMiddleware.h" 11 | 12 | /** 13 | * NSNotification name, that is posted after integrations are loaded. 14 | */ 15 | extern NSString *_Nonnull SEGAnalyticsIntegrationDidStart; 16 | 17 | @class SEGAnalytics; 18 | 19 | 20 | @interface SEGIntegrationsManager : NSObject 21 | 22 | // Exposed for testing. 23 | + (BOOL)isTrackEvent:(NSString *_Nonnull)event enabledForIntegration:(NSString *_Nonnull)key inPlan:(NSDictionary *_Nonnull)plan; 24 | 25 | // @Deprecated - Exposing for backward API compat reasons only 26 | @property (nonatomic, readonly) NSMutableDictionary *_Nonnull registeredIntegrations; 27 | 28 | - (instancetype _Nonnull)initWithAnalytics:(SEGAnalytics *_Nonnull)analytics; 29 | 30 | // @Deprecated - Exposing for backward API compat reasons only 31 | - (NSString *_Nonnull)getAnonymousId; 32 | 33 | @end 34 | 35 | 36 | @interface SEGIntegrationsManager (SEGMiddleware) 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGSerializableValue.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGPayload : NSObject 8 | 9 | @property (nonatomic, readonly) JSON_DICT context; 10 | @property (nonatomic, readonly) JSON_DICT integrations; 11 | 12 | - (instancetype)initWithContext:(JSON_DICT)context integrations:(JSON_DICT)integrations; 13 | 14 | @end 15 | 16 | 17 | @interface SEGApplicationLifecyclePayload : SEGPayload 18 | 19 | @property (nonatomic, strong) NSString *notificationName; 20 | 21 | // ApplicationDidFinishLaunching only 22 | @property (nonatomic, strong, nullable) NSDictionary *launchOptions; 23 | 24 | @end 25 | 26 | 27 | @interface SEGContinueUserActivityPayload : SEGPayload 28 | 29 | @property (nonatomic, strong) NSUserActivity *activity; 30 | 31 | @end 32 | 33 | 34 | @interface SEGOpenURLPayload : SEGPayload 35 | 36 | @property (nonatomic, strong) NSURL *url; 37 | @property (nonatomic, strong) NSDictionary *options; 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | 43 | 44 | @interface SEGRemoteNotificationPayload : SEGPayload 45 | 46 | // SEGEventTypeHandleActionWithForRemoteNotification 47 | @property (nonatomic, strong, nullable) NSString *actionIdentifier; 48 | 49 | // SEGEventTypeHandleActionWithForRemoteNotification 50 | // SEGEventTypeReceivedRemoteNotification 51 | @property (nonatomic, strong, nullable) NSDictionary *userInfo; 52 | 53 | // SEGEventTypeFailedToRegisterForRemoteNotifications 54 | @property (nonatomic, strong, nullable) NSError *error; 55 | 56 | // SEGEventTypeRegisteredForRemoteNotifications 57 | @property (nonatomic, strong, nullable) NSData *deviceToken; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGPayload.h" 2 | 3 | 4 | @implementation SEGPayload 5 | 6 | - (instancetype)initWithContext:(NSDictionary *)context integrations:(NSDictionary *)integrations 7 | { 8 | if (self = [super init]) { 9 | _context = [context copy]; 10 | _integrations = [integrations copy]; 11 | } 12 | return self; 13 | } 14 | 15 | @end 16 | 17 | 18 | @implementation SEGApplicationLifecyclePayload 19 | 20 | @end 21 | 22 | 23 | @implementation SEGRemoteNotificationPayload 24 | 25 | @end 26 | 27 | 28 | @implementation SEGContinueUserActivityPayload 29 | 30 | @end 31 | 32 | 33 | @implementation SEGOpenURLPayload 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGScreenPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGPayload.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGScreenPayload : SEGPayload 8 | 9 | @property (nonatomic, readonly) NSString *name; 10 | 11 | @property (nonatomic, readonly, nullable) NSString *category; 12 | 13 | @property (nonatomic, readonly, nullable) NSDictionary *properties; 14 | 15 | - (instancetype)initWithName:(NSString *)name 16 | properties:(NSDictionary *_Nullable)properties 17 | context:(NSDictionary *)context 18 | integrations:(NSDictionary *)integrations; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGScreenPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGScreenPayload.h" 2 | 3 | 4 | @implementation SEGScreenPayload 5 | 6 | - (instancetype)initWithName:(NSString *)name 7 | properties:(NSDictionary *)properties 8 | context:(NSDictionary *)context 9 | integrations:(NSDictionary *)integrations 10 | { 11 | if (self = [super initWithContext:context integrations:integrations]) { 12 | _name = [name copy]; 13 | _properties = [properties copy]; 14 | } 15 | return self; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGTrackPayload.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGPayload.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | 7 | @interface SEGTrackPayload : SEGPayload 8 | 9 | @property (nonatomic, readonly) NSString *event; 10 | 11 | @property (nonatomic, readonly, nullable) NSDictionary *properties; 12 | 13 | - (instancetype)initWithEvent:(NSString *)event 14 | properties:(NSDictionary *_Nullable)properties 15 | context:(NSDictionary *)context 16 | integrations:(NSDictionary *)integrations; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Integrations/SEGTrackPayload.m: -------------------------------------------------------------------------------- 1 | #import "SEGTrackPayload.h" 2 | 3 | 4 | @implementation SEGTrackPayload 5 | 6 | 7 | - (instancetype)initWithEvent:(NSString *)event 8 | properties:(NSDictionary *)properties 9 | context:(NSDictionary *)context 10 | integrations:(NSDictionary *)integrations 11 | { 12 | if (self = [super initWithContext:context integrations:integrations]) { 13 | _event = [event copy]; 14 | _properties = [properties copy]; 15 | } 16 | return self; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/NSData+SEGGZIP.h: -------------------------------------------------------------------------------- 1 | // 2 | // GZIP.h 3 | // 4 | // Version 1.1.1 5 | // 6 | // Created by Nick Lockwood on 03/06/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/GZIP 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import 35 | 36 | extern void *_Nullable seg_libzOpen(void); 37 | 38 | 39 | @interface NSData (GZIP) 40 | 41 | - (nullable NSData *)seg_gzippedData; 42 | - (BOOL)seg_isGzippedData; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/NSData+SEGGZIP.m: -------------------------------------------------------------------------------- 1 | // 2 | // GZIP.m 3 | // 4 | // Version 1.1.1 5 | // 6 | // Created by Nick Lockwood on 03/06/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/GZIP 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import "NSData+SEGGZIP.h" 35 | #import 36 | #import 37 | 38 | 39 | #pragma clang diagnostic ignored "-Wcast-qual" 40 | 41 | void *_Nullable seg_libzOpen() 42 | { 43 | static void *libz; 44 | static dispatch_once_t onceToken; 45 | dispatch_once(&onceToken, ^{ 46 | libz = dlopen("/usr/lib/libz.dylib", RTLD_LAZY); 47 | }); 48 | return libz; 49 | } 50 | 51 | 52 | @implementation NSData (GZIP) 53 | 54 | - (NSData *)seg_gzippedDataWithCompressionLevel:(float)level 55 | { 56 | if (self.length == 0 || [self seg_isGzippedData]) { 57 | return self; 58 | } 59 | 60 | void *libz = seg_libzOpen(); 61 | int (*deflateInit2_)(z_streamp, int, int, int, int, int, const char *, int) = 62 | (int (*)(z_streamp, int, int, int, int, int, const char *, int))dlsym(libz, "deflateInit2_"); 63 | int (*deflate)(z_streamp, int) = (int (*)(z_streamp, int))dlsym(libz, "deflate"); 64 | int (*deflateEnd)(z_streamp) = (int (*)(z_streamp))dlsym(libz, "deflateEnd"); 65 | 66 | z_stream stream; 67 | stream.zalloc = Z_NULL; 68 | stream.zfree = Z_NULL; 69 | stream.opaque = Z_NULL; 70 | stream.avail_in = (uint)self.length; 71 | stream.next_in = (Bytef *)(void *)self.bytes; 72 | stream.total_out = 0; 73 | stream.avail_out = 0; 74 | 75 | static const NSUInteger ChunkSize = 16384; 76 | 77 | NSMutableData *output = nil; 78 | int compression = (level < 0.0f) ? Z_DEFAULT_COMPRESSION : (int)(roundf(level * 9)); 79 | if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) { 80 | output = [NSMutableData dataWithLength:ChunkSize]; 81 | while (stream.avail_out == 0) { 82 | if (stream.total_out >= output.length) { 83 | output.length += ChunkSize; 84 | } 85 | stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out; 86 | stream.avail_out = (uInt)(output.length - stream.total_out); 87 | deflate(&stream, Z_FINISH); 88 | } 89 | deflateEnd(&stream); 90 | output.length = stream.total_out; 91 | } 92 | 93 | return output; 94 | } 95 | 96 | - (NSData *)seg_gzippedData 97 | { 98 | return [self seg_gzippedDataWithCompressionLevel:-1.0f]; 99 | } 100 | 101 | - (BOOL)seg_isGzippedData 102 | { 103 | const UInt8 *bytes = (const UInt8 *)self.bytes; 104 | return (self.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b); 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGAnalyticsUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | NSString *GenerateUUIDString(void); 7 | 8 | // Date Utils 9 | NSString *iso8601FormattedString(NSDate *date); 10 | 11 | // Async Utils 12 | dispatch_queue_t seg_dispatch_queue_create_specific(const char *label, 13 | dispatch_queue_attr_t _Nullable attr); 14 | BOOL seg_dispatch_is_on_specific_queue(dispatch_queue_t queue); 15 | void seg_dispatch_specific(dispatch_queue_t queue, dispatch_block_t block, 16 | BOOL waitForCompletion); 17 | void seg_dispatch_specific_async(dispatch_queue_t queue, 18 | dispatch_block_t block); 19 | void seg_dispatch_specific_sync(dispatch_queue_t queue, dispatch_block_t block); 20 | 21 | // Logging 22 | 23 | void SEGSetShowDebugLogs(BOOL showDebugLogs); 24 | void SEGLog(NSString *format, ...); 25 | 26 | // JSON Utils 27 | 28 | JSON_DICT SEGCoerceDictionary(NSDictionary *_Nullable dict); 29 | 30 | NSString *_Nullable SEGIDFA(void); 31 | 32 | NSString *SEGEventNameForScreenTitle(NSString *title); 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGAnalyticsUtils.m: -------------------------------------------------------------------------------- 1 | #import "SEGAnalyticsUtils.h" 2 | #import 3 | 4 | static BOOL kAnalyticsLoggerShowLogs = NO; 5 | 6 | NSString *GenerateUUIDString() 7 | { 8 | CFUUIDRef theUUID = CFUUIDCreate(NULL); 9 | NSString *UUIDString = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theUUID); 10 | CFRelease(theUUID); 11 | return UUIDString; 12 | } 13 | 14 | // Date Utils 15 | NSString *iso8601FormattedString(NSDate *date) 16 | { 17 | static NSDateFormatter *dateFormatter; 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | dateFormatter = [[NSDateFormatter alloc] init]; 21 | dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; 22 | dateFormatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'"; 23 | dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; 24 | }); 25 | return [dateFormatter stringFromDate:date]; 26 | } 27 | 28 | // Async Utils 29 | dispatch_queue_t 30 | seg_dispatch_queue_create_specific(const char *label, 31 | dispatch_queue_attr_t attr) 32 | { 33 | dispatch_queue_t queue = dispatch_queue_create(label, attr); 34 | dispatch_queue_set_specific(queue, (__bridge const void *)queue, 35 | (__bridge void *)queue, NULL); 36 | return queue; 37 | } 38 | 39 | BOOL seg_dispatch_is_on_specific_queue(dispatch_queue_t queue) 40 | { 41 | return dispatch_get_specific((__bridge const void *)queue) != NULL; 42 | } 43 | 44 | void seg_dispatch_specific(dispatch_queue_t queue, dispatch_block_t block, 45 | BOOL waitForCompletion) 46 | { 47 | dispatch_block_t autoreleasing_block = ^{ 48 | @autoreleasepool 49 | { 50 | block(); 51 | } 52 | }; 53 | if (dispatch_get_specific((__bridge const void *)queue)) { 54 | autoreleasing_block(); 55 | } else if (waitForCompletion) { 56 | dispatch_sync(queue, autoreleasing_block); 57 | } else { 58 | dispatch_async(queue, autoreleasing_block); 59 | } 60 | } 61 | 62 | void seg_dispatch_specific_async(dispatch_queue_t queue, 63 | dispatch_block_t block) 64 | { 65 | seg_dispatch_specific(queue, block, NO); 66 | } 67 | 68 | void seg_dispatch_specific_sync(dispatch_queue_t queue, 69 | dispatch_block_t block) 70 | { 71 | seg_dispatch_specific(queue, block, YES); 72 | } 73 | 74 | // Logging 75 | 76 | void SEGSetShowDebugLogs(BOOL showDebugLogs) 77 | { 78 | kAnalyticsLoggerShowLogs = showDebugLogs; 79 | } 80 | 81 | void SEGLog(NSString *format, ...) 82 | { 83 | if (!kAnalyticsLoggerShowLogs) 84 | return; 85 | 86 | va_list args; 87 | va_start(args, format); 88 | NSLogv(format, args); 89 | va_end(args); 90 | } 91 | 92 | // JSON Utils 93 | 94 | static id SEGCoerceJSONObject(id obj) 95 | { 96 | // Hotfix: Storage format should support NSNull instead 97 | if ([obj isKindOfClass:[NSNull class]]) { 98 | return @""; 99 | } 100 | // if the object is a NSString, NSNumber 101 | // then we're good 102 | if ([obj isKindOfClass:[NSString class]] || 103 | [obj isKindOfClass:[NSNumber class]]) { 104 | return obj; 105 | } 106 | 107 | if ([obj isKindOfClass:[NSArray class]]) { 108 | NSMutableArray *array = [NSMutableArray array]; 109 | for (id i in obj) { 110 | // Hotfix: Storage format should support NSNull instead 111 | if ([i isKindOfClass:[NSNull class]]) { 112 | continue; 113 | } 114 | [array addObject:SEGCoerceJSONObject(i)]; 115 | } 116 | return array; 117 | } 118 | 119 | if ([obj isKindOfClass:[NSDictionary class]]) { 120 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 121 | for (NSString *key in obj) { 122 | // Hotfix for issue where SEGFileStorage uses plist which does NOT support NSNull 123 | // So when `[NSNull null]` gets passed in as track property values the queue serialization fails 124 | if ([obj[key] isKindOfClass:[NSNull class]]) { 125 | continue; 126 | } 127 | if (![key isKindOfClass:[NSString class]]) 128 | SEGLog(@"warning: dictionary keys should be strings. got: %@. coercing " 129 | @"to: %@", 130 | [key class], [key description]); 131 | dict[key.description] = SEGCoerceJSONObject(obj[key]); 132 | } 133 | return dict; 134 | } 135 | 136 | if ([obj isKindOfClass:[NSDate class]]) 137 | return iso8601FormattedString(obj); 138 | 139 | if ([obj isKindOfClass:[NSURL class]]) 140 | return [obj absoluteString]; 141 | 142 | // default to sending the object's description 143 | SEGLog(@"warning: dictionary values should be valid json types. got: %@. " 144 | @"coercing to: %@", 145 | [obj class], [obj description]); 146 | return [obj description]; 147 | } 148 | 149 | static void AssertDictionaryTypes(id dict) 150 | { 151 | #ifdef DEBUG 152 | assert([dict isKindOfClass:[NSDictionary class]]); 153 | for (id key in dict) { 154 | assert([key isKindOfClass:[NSString class]]); 155 | id value = dict[key]; 156 | 157 | assert([value isKindOfClass:[NSString class]] || 158 | [value isKindOfClass:[NSNumber class]] || 159 | [value isKindOfClass:[NSNull class]] || 160 | [value isKindOfClass:[NSArray class]] || 161 | [value isKindOfClass:[NSDictionary class]] || 162 | [value isKindOfClass:[NSDate class]] || 163 | [value isKindOfClass:[NSURL class]]); 164 | } 165 | #endif 166 | } 167 | 168 | NSDictionary *SEGCoerceDictionary(NSDictionary *dict) 169 | { 170 | // make sure that a new dictionary exists even if the input is null 171 | dict = dict ?: @{}; 172 | // assert that the proper types are in the dictionary 173 | AssertDictionaryTypes(dict); 174 | // coerce urls, and dates to the proper format 175 | return SEGCoerceJSONObject(dict); 176 | } 177 | 178 | NSString *SEGIDFA() 179 | { 180 | NSString *idForAdvertiser = nil; 181 | Class identifierManager = NSClassFromString(@"ASIdentifierManager"); 182 | if (identifierManager) { 183 | SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager"); 184 | id sharedManager = 185 | ((id (*)(id, SEL)) 186 | [identifierManager methodForSelector:sharedManagerSelector])( 187 | identifierManager, sharedManagerSelector); 188 | SEL advertisingIdentifierSelector = 189 | NSSelectorFromString(@"advertisingIdentifier"); 190 | NSUUID *uuid = 191 | ((NSUUID * (*)(id, SEL)) 192 | [sharedManager methodForSelector:advertisingIdentifierSelector])( 193 | sharedManager, advertisingIdentifierSelector); 194 | idForAdvertiser = [uuid UUIDString]; 195 | } 196 | return idForAdvertiser; 197 | } 198 | 199 | NSString *SEGEventNameForScreenTitle(NSString *title) 200 | { 201 | return [[NSString alloc] initWithFormat:@"Viewed %@ Screen", title]; 202 | } 203 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGFileStorage.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGFileStorage.h 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "SEGStorage.h" 10 | 11 | 12 | @interface SEGFileStorage : NSObject 13 | 14 | @property (nonatomic, strong, nullable) id crypto; 15 | 16 | - (instancetype _Nonnull)init; 17 | - (instancetype _Nonnull)initWithFolder:(NSURL *_Nonnull)folderURL crypto:(id _Nullable)crypto; 18 | 19 | - (NSURL *_Nonnull)urlForKey:(NSString *_Nonnull)key; 20 | 21 | + (NSURL *_Nullable)applicationSupportDirectoryURL; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGFileStorage.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGFileStorage.m 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | #import "SEGUtils.h" 9 | #import "SEGFileStorage.h" 10 | #import "SEGCrypto.h" 11 | 12 | 13 | @interface SEGFileStorage () 14 | 15 | @property (nonatomic, strong, nonnull) NSURL *folderURL; 16 | 17 | @end 18 | 19 | 20 | @implementation SEGFileStorage 21 | 22 | - (instancetype)init 23 | { 24 | return [self initWithFolder:[SEGFileStorage applicationSupportDirectoryURL] crypto:nil]; 25 | } 26 | 27 | - (instancetype)initWithFolder:(NSURL *)folderURL crypto:(id)crypto 28 | { 29 | if (self = [super init]) { 30 | _folderURL = folderURL; 31 | _crypto = crypto; 32 | [self createDirectoryAtURLIfNeeded:folderURL]; 33 | return self; 34 | } 35 | return nil; 36 | } 37 | 38 | - (void)removeKey:(NSString *)key 39 | { 40 | NSURL *url = [self urlForKey:key]; 41 | NSError *error = nil; 42 | if (![[NSFileManager defaultManager] removeItemAtURL:url error:&error]) { 43 | SEGLog(@"Unable to remove key %@ - error removing file at path %@", key, url); 44 | } 45 | } 46 | 47 | - (void)resetAll 48 | { 49 | NSError *error = nil; 50 | if (![[NSFileManager defaultManager] removeItemAtURL:self.folderURL error:&error]) { 51 | SEGLog(@"ERROR: Unable to reset file storage. Path cannot be removed - %@", self.folderURL.path); 52 | } 53 | [self createDirectoryAtURLIfNeeded:self.folderURL]; 54 | } 55 | 56 | - (void)setData:(NSData *)data forKey:(NSString *)key 57 | { 58 | NSURL *url = [self urlForKey:key]; 59 | if (self.crypto) { 60 | NSData *encryptedData = [self.crypto encrypt:data]; 61 | [encryptedData writeToURL:url atomically:YES]; 62 | } else { 63 | [data writeToURL:url atomically:YES]; 64 | } 65 | 66 | NSError *error = nil; 67 | if (![url setResourceValue:@YES 68 | forKey:NSURLIsExcludedFromBackupKey 69 | error:&error]) { 70 | SEGLog(@"Error excluding %@ from backup %@", [url lastPathComponent], error); 71 | } 72 | } 73 | 74 | - (NSData *)dataForKey:(NSString *)key 75 | { 76 | NSURL *url = [self urlForKey:key]; 77 | NSData *data = [NSData dataWithContentsOfURL:url]; 78 | if (!data) { 79 | SEGLog(@"WARNING: No data file for key %@", key); 80 | return nil; 81 | } 82 | if (self.crypto) { 83 | return [self.crypto decrypt:data]; 84 | } 85 | return data; 86 | } 87 | 88 | - (NSDictionary *)dictionaryForKey:(NSString *)key 89 | { 90 | return [self plistForKey:key]; 91 | } 92 | 93 | - (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key 94 | { 95 | [self setPlist:dictionary forKey:key]; 96 | } 97 | 98 | - (NSArray *)arrayForKey:(NSString *)key 99 | { 100 | return [self plistForKey:key]; 101 | } 102 | 103 | - (void)setArray:(NSArray *)array forKey:(NSString *)key 104 | { 105 | [self setPlist:array forKey:key]; 106 | } 107 | 108 | - (NSString *)stringForKey:(NSString *)key 109 | { 110 | return [self plistForKey:key]; 111 | } 112 | 113 | - (void)setString:(NSString *)string forKey:(NSString *)key 114 | { 115 | [self setPlist:string forKey:key]; 116 | } 117 | 118 | + (NSURL *)applicationSupportDirectoryURL 119 | { 120 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 121 | NSString *supportPath = [paths firstObject]; 122 | return [NSURL fileURLWithPath:supportPath]; 123 | } 124 | 125 | - (NSURL *)urlForKey:(NSString *)key 126 | { 127 | return [self.folderURL URLByAppendingPathComponent:key]; 128 | } 129 | 130 | #pragma mark - Helpers 131 | 132 | - (id _Nullable)plistForKey:(NSString *)key 133 | { 134 | NSData *data = [self dataForKey:key]; 135 | return data ? [self plistFromData:data] : nil; 136 | } 137 | 138 | - (void)setPlist:(id _Nonnull)plist forKey:(NSString *)key 139 | { 140 | NSData *data = [self dataFromPlist:plist]; 141 | if (data) { 142 | [self setData:data forKey:key]; 143 | } 144 | } 145 | 146 | - (NSData *_Nullable)dataFromPlist:(nonnull id)plist 147 | { 148 | NSError *error = nil; 149 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist 150 | format:NSPropertyListXMLFormat_v1_0 151 | options:0 152 | error:&error]; 153 | if (error) { 154 | SEGLog(@"Unable to serialize data from plist object", error, plist); 155 | } 156 | return data; 157 | } 158 | 159 | - (id _Nullable)plistFromData:(NSData *_Nonnull)data 160 | { 161 | NSError *error = nil; 162 | id plist = [NSPropertyListSerialization propertyListWithData:data 163 | options:0 164 | format:nil 165 | error:&error]; 166 | if (error) { 167 | SEGLog(@"Unable to parse plist from data %@", error); 168 | } 169 | return plist; 170 | } 171 | 172 | - (void)createDirectoryAtURLIfNeeded:(NSURL *)url 173 | { 174 | if (![[NSFileManager defaultManager] fileExistsAtPath:url.path 175 | isDirectory:NULL]) { 176 | NSError *error = nil; 177 | if (![[NSFileManager defaultManager] createDirectoryAtPath:url.path 178 | withIntermediateDirectories:YES 179 | attributes:nil 180 | error:&error]) { 181 | SEGLog(@"error: %@", error.localizedDescription); 182 | } 183 | } 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGHTTPClient.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGAnalytics.h" 3 | 4 | // TODO: Make this configurable via SEGAnalyticsConfiguration 5 | // NOTE: `/` at the end kind of screws things up. So don't use it 6 | //#define SEGMENT_API_BASE [NSURL URLWithString:@"https://api-segment-io-5fsaj1xnikhp.runscope.net/v1"] 7 | //#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-segment-com-5fsaj1xnikhp.runscope.net/v1"] 8 | //#define MOBILE_SERVICE_BASE [NSURL URLWithString:@"https://mobile--service-segment-com-5fsaj1xnikhp.runscope.net/v1"] 9 | #define SEGMENT_API_BASE [NSURL URLWithString:@"https://api.segment.io/v1"] 10 | #define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-settings.segment.com/v1"] 11 | #define MOBILE_SERVICE_BASE [NSURL URLWithString:@"https://mobile-service.segment.com/v1"] 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | @interface SEGHTTPClient : NSObject 17 | 18 | @property (nonatomic, strong) SEGRequestFactory requestFactory; 19 | @property (nonatomic, readonly) NSMutableDictionary *sessionsByWriteKey; 20 | @property (nonatomic, readonly) NSURLSession *genericSession; 21 | 22 | + (SEGRequestFactory)defaultRequestFactory; 23 | + (NSString *)authorizationHeader:(NSString *)writeKey; 24 | 25 | - (instancetype)initWithRequestFactory:(SEGRequestFactory _Nullable)requestFactory; 26 | 27 | /** 28 | * Upload dictionary formatted as per https://segment.com/docs/sources/server/http/#batch. 29 | * This method will convert the dictionary to json, gzip it and upload the data. 30 | * It will respond with retry = YES if the batch should be reuploaded at a later time. 31 | * It will ask to retry for json errors and 3xx/5xx codes, and not retry for 2xx/4xx response codes. 32 | * NOTE: You need to re-dispatch within the completionHandler onto a desired queue to avoid threading issues. 33 | * Completion handlers are called on a dispatch queue internal to SEGHTTPClient. 34 | */ 35 | - (NSURLSessionUploadTask *)upload:(JSON_DICT)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler; 36 | 37 | - (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL success, JSON_DICT _Nullable settings))completionHandler; 38 | 39 | - (NSURLSessionDataTask *)attributionWithWriteKey:(NSString *)writeKey forDevice:(JSON_DICT)context completionHandler:(void (^)(BOOL success, JSON_DICT _Nullable properties))completionHandler; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGHTTPClient.m: -------------------------------------------------------------------------------- 1 | #import "SEGHTTPClient.h" 2 | #import "NSData+SEGGZIP.h" 3 | #import "SEGAnalyticsUtils.h" 4 | 5 | 6 | @implementation SEGHTTPClient 7 | 8 | + (NSMutableURLRequest * (^)(NSURL *))defaultRequestFactory 9 | { 10 | return ^(NSURL *url) { 11 | return [NSMutableURLRequest requestWithURL:url]; 12 | }; 13 | } 14 | 15 | + (NSString *)authorizationHeader:(NSString *)writeKey 16 | { 17 | NSString *rawHeader = [writeKey stringByAppendingString:@":"]; 18 | NSData *userPasswordData = [rawHeader dataUsingEncoding:NSUTF8StringEncoding]; 19 | return [userPasswordData base64EncodedStringWithOptions:0]; 20 | } 21 | 22 | 23 | - (instancetype)initWithRequestFactory:(SEGRequestFactory)requestFactory 24 | { 25 | if (self = [self init]) { 26 | if (requestFactory == nil) { 27 | self.requestFactory = [SEGHTTPClient defaultRequestFactory]; 28 | } else { 29 | self.requestFactory = requestFactory; 30 | } 31 | _sessionsByWriteKey = [NSMutableDictionary dictionary]; 32 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 33 | config.HTTPAdditionalHeaders = @{ @"Accept-Encoding" : @"gzip" }; 34 | _genericSession = [NSURLSession sessionWithConfiguration:config]; 35 | } 36 | return self; 37 | } 38 | 39 | - (NSURLSession *)sessionForWriteKey:(NSString *)writeKey 40 | { 41 | NSURLSession *session = self.sessionsByWriteKey[writeKey]; 42 | if (!session) { 43 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 44 | config.HTTPAdditionalHeaders = @{ 45 | @"Accept-Encoding" : @"gzip", 46 | @"Content-Encoding" : @"gzip", 47 | @"Content-Type" : @"application/json", 48 | @"Authorization" : [@"Basic " stringByAppendingString:[[self class] authorizationHeader:writeKey]], 49 | }; 50 | session = [NSURLSession sessionWithConfiguration:config]; 51 | self.sessionsByWriteKey[writeKey] = session; 52 | } 53 | return session; 54 | } 55 | 56 | - (void)dealloc 57 | { 58 | for (NSURLSession *session in self.sessionsByWriteKey.allValues) { 59 | [session finishTasksAndInvalidate]; 60 | } 61 | [self.genericSession finishTasksAndInvalidate]; 62 | } 63 | 64 | 65 | - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler 66 | { 67 | // batch = SEGCoerceDictionary(batch); 68 | NSURLSession *session = [self sessionForWriteKey:writeKey]; 69 | 70 | NSURL *url = [SEGMENT_API_BASE URLByAppendingPathComponent:@"batch"]; 71 | NSMutableURLRequest *request = self.requestFactory(url); 72 | 73 | // This is a workaround for an IOS 8.3 bug that causes Content-Type to be incorrectly set 74 | [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; 75 | 76 | [request setHTTPMethod:@"POST"]; 77 | 78 | NSError *error = nil; 79 | NSException *exception = nil; 80 | NSData *payload = nil; 81 | @try { 82 | payload = [NSJSONSerialization dataWithJSONObject:batch options:0 error:&error]; 83 | } 84 | @catch (NSException *exc) { 85 | exception = exc; 86 | } 87 | if (error || exception) { 88 | SEGLog(@"Error serializing JSON for batch upload %@", error); 89 | completionHandler(NO); // Don't retry this batch. 90 | return nil; 91 | } 92 | NSData *gzippedPayload = [payload seg_gzippedData]; 93 | 94 | NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:gzippedPayload completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { 95 | if (error) { 96 | SEGLog(@"Error uploading request %@.", error); 97 | completionHandler(YES); 98 | return; 99 | } 100 | 101 | NSInteger code = ((NSHTTPURLResponse *)response).statusCode; 102 | if (code < 300) { 103 | // 2xx response codes. 104 | completionHandler(NO); 105 | return; 106 | } 107 | if (code < 400) { 108 | // 3xx response codes. 109 | SEGLog(@"Server responded with unexpected HTTP code %d.", code); 110 | completionHandler(YES); 111 | return; 112 | } 113 | if (code < 500) { 114 | // 4xx response codes. 115 | SEGLog(@"Server rejected payload with HTTP code %d.", code); 116 | completionHandler(NO); 117 | return; 118 | } 119 | 120 | // 5xx response codes. 121 | SEGLog(@"Server error with HTTP code %d.", code); 122 | completionHandler(YES); 123 | }]; 124 | [task resume]; 125 | return task; 126 | } 127 | 128 | - (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL success, JSON_DICT _Nullable settings))completionHandler 129 | { 130 | NSURLSession *session = self.genericSession; 131 | 132 | NSURL *url = [SEGMENT_CDN_BASE URLByAppendingPathComponent:[NSString stringWithFormat:@"/projects/%@/settings", writeKey]]; 133 | NSMutableURLRequest *request = self.requestFactory(url); 134 | [request setHTTPMethod:@"GET"]; 135 | 136 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { 137 | if (error != nil) { 138 | SEGLog(@"Error fetching settings %@.", error); 139 | completionHandler(NO, nil); 140 | return; 141 | } 142 | 143 | NSInteger code = ((NSHTTPURLResponse *)response).statusCode; 144 | if (code > 300) { 145 | SEGLog(@"Server responded with unexpected HTTP code %d.", code); 146 | completionHandler(NO, nil); 147 | return; 148 | } 149 | 150 | NSError *jsonError = nil; 151 | id responseJson = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; 152 | if (jsonError != nil) { 153 | SEGLog(@"Error deserializing response body %@.", jsonError); 154 | completionHandler(NO, nil); 155 | return; 156 | } 157 | 158 | completionHandler(YES, responseJson); 159 | }]; 160 | [task resume]; 161 | return task; 162 | } 163 | 164 | - (NSURLSessionDataTask *)attributionWithWriteKey:(NSString *)writeKey forDevice:(JSON_DICT)context completionHandler:(void (^)(BOOL success, JSON_DICT _Nullable properties))completionHandler; 165 | 166 | { 167 | NSURLSession *session = [self sessionForWriteKey:writeKey]; 168 | 169 | NSURL *url = [MOBILE_SERVICE_BASE URLByAppendingPathComponent:@"/attribution"]; 170 | NSMutableURLRequest *request = self.requestFactory(url); 171 | [request setHTTPMethod:@"POST"]; 172 | 173 | NSError *error = nil; 174 | NSException *exception = nil; 175 | NSData *payload = nil; 176 | @try { 177 | payload = [NSJSONSerialization dataWithJSONObject:context options:0 error:&error]; 178 | } 179 | @catch (NSException *exc) { 180 | exception = exc; 181 | } 182 | if (error || exception) { 183 | SEGLog(@"Error serializing context to JSON %@", error); 184 | completionHandler(NO, nil); 185 | return nil; 186 | } 187 | NSData *gzippedPayload = [payload seg_gzippedData]; 188 | 189 | NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:gzippedPayload completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { 190 | if (error) { 191 | SEGLog(@"Error making request %@.", error); 192 | completionHandler(NO, nil); 193 | return; 194 | } 195 | 196 | NSInteger code = ((NSHTTPURLResponse *)response).statusCode; 197 | if (code > 300) { 198 | SEGLog(@"Server responded with unexpected HTTP code %d.", code); 199 | completionHandler(NO, nil); 200 | return; 201 | } 202 | 203 | NSError *jsonError = nil; 204 | id responseJson = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; 205 | if (jsonError != nil) { 206 | SEGLog(@"Error deserializing response body %@.", jsonError); 207 | completionHandler(NO, nil); 208 | return; 209 | } 210 | 211 | completionHandler(YES, responseJson); 212 | }]; 213 | [task resume]; 214 | return task; 215 | } 216 | 217 | @end 218 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGSegmentIntegration.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGIntegration.h" 3 | #import "SEGHTTPClient.h" 4 | #import "SEGStorage.h" 5 | 6 | NS_ASSUME_NONNULL_BEGIN 7 | 8 | extern NSString *const SEGSegmentDidSendRequestNotification; 9 | extern NSString *const SEGSegmentRequestDidSucceedNotification; 10 | extern NSString *const SEGSegmentRequestDidFailNotification; 11 | 12 | 13 | @interface SEGSegmentIntegration : NSObject 14 | 15 | - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)httpClient storage:(id)storage; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGSegmentIntegrationFactory.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGIntegrationFactory.h" 3 | #import "SEGHTTPClient.h" 4 | #import "SEGStorage.h" 5 | 6 | NS_ASSUME_NONNULL_BEGIN 7 | 8 | 9 | @interface SEGSegmentIntegrationFactory : NSObject 10 | 11 | @property (nonatomic, strong) SEGHTTPClient *client; 12 | @property (nonatomic, strong) id storage; 13 | 14 | - (instancetype)initWithHTTPClient:(SEGHTTPClient *)client storage:(id)storage; 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGSegmentIntegrationFactory.m: -------------------------------------------------------------------------------- 1 | #import "SEGSegmentIntegrationFactory.h" 2 | #import "SEGSegmentIntegration.h" 3 | 4 | 5 | @implementation SEGSegmentIntegrationFactory 6 | 7 | - (id)initWithHTTPClient:(SEGHTTPClient *)client storage:(id)storage 8 | { 9 | if (self = [super init]) { 10 | _client = client; 11 | _storage = storage; 12 | } 13 | return self; 14 | } 15 | 16 | - (id)createWithSettings:(NSDictionary *)settings forAnalytics:(SEGAnalytics *)analytics 17 | { 18 | return [[SEGSegmentIntegration alloc] initWithAnalytics:analytics httpClient:self.client storage:self.storage]; 19 | } 20 | 21 | - (NSString *)key 22 | { 23 | return @"Segment.io"; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGStorage.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGStorage.h 3 | // Analytics 4 | // 5 | // Copyright © 2016 Segment. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "SEGCrypto.h" 10 | 11 | @protocol SEGStorage 12 | 13 | @property (nonatomic, strong, nullable) id crypto; 14 | 15 | - (void)removeKey:(NSString *_Nonnull)key; 16 | - (void)resetAll; 17 | 18 | - (void)setData:(NSData *_Nonnull)data forKey:(NSString *_Nonnull)key; 19 | - (NSData *_Nullable)dataForKey:(NSString *_Nonnull)key; 20 | 21 | - (void)setDictionary:(NSDictionary *_Nonnull)dictionary forKey:(NSString *_Nonnull)key; 22 | - (NSDictionary *_Nullable)dictionaryForKey:(NSString *_Nonnull)key; 23 | 24 | - (void)setArray:(NSArray *_Nonnull)array forKey:(NSString *_Nonnull)key; 25 | - (NSArray *_Nullable)arrayForKey:(NSString *_Nonnull)key; 26 | 27 | - (void)setString:(NSString *_Nonnull)string forKey:(NSString *_Nonnull)key; 28 | - (NSString *_Nullable)stringForKey:(NSString *_Nonnull)key; 29 | 30 | // Number and Booleans are intentionally omitted at the moment because they are not needed 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGStoreKitTracker.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "SEGAnalytics.h" 4 | 5 | NS_ASSUME_NONNULL_BEGIN 6 | 7 | 8 | @interface SEGStoreKitTracker : NSObject 9 | 10 | + (instancetype)trackTransactionsForAnalytics:(SEGAnalytics *)analytics; 11 | 12 | @end 13 | 14 | NS_ASSUME_NONNULL_END 15 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGStoreKitTracker.m: -------------------------------------------------------------------------------- 1 | #import "SEGStoreKitTracker.h" 2 | 3 | 4 | @interface SEGStoreKitTracker () 5 | 6 | @property (nonatomic, readonly) SEGAnalytics *analytics; 7 | @property (nonatomic, readonly) NSMutableDictionary *transactions; 8 | @property (nonatomic, readonly) NSMutableDictionary *productRequests; 9 | 10 | @end 11 | 12 | 13 | @implementation SEGStoreKitTracker 14 | 15 | + (instancetype)trackTransactionsForAnalytics:(SEGAnalytics *)analytics 16 | { 17 | return [[SEGStoreKitTracker alloc] initWithAnalytics:analytics]; 18 | } 19 | 20 | - (instancetype)initWithAnalytics:(SEGAnalytics *)analytics 21 | { 22 | if (self = [self init]) { 23 | _analytics = analytics; 24 | _productRequests = [NSMutableDictionary dictionaryWithCapacity:1]; 25 | _transactions = [NSMutableDictionary dictionaryWithCapacity:1]; 26 | 27 | [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)dealloc 33 | { 34 | [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; 35 | } 36 | 37 | #pragma mark - SKPaymentQueue Observer 38 | - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions 39 | { 40 | for (SKPaymentTransaction *transaction in transactions) { 41 | if (transaction.transactionState != SKPaymentTransactionStatePurchased) { 42 | continue; 43 | } 44 | 45 | SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:transaction.payment.productIdentifier]]; 46 | @synchronized(self) 47 | { 48 | [self.transactions setObject:transaction forKey:transaction.payment.productIdentifier]; 49 | [self.productRequests setObject:request forKey:transaction.payment.productIdentifier]; 50 | } 51 | request.delegate = self; 52 | [request start]; 53 | } 54 | } 55 | 56 | #pragma mark - SKProductsRequest delegate 57 | - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response 58 | { 59 | for (SKProduct *product in response.products) { 60 | @synchronized(self) 61 | { 62 | SKPaymentTransaction *transaction = [self.transactions objectForKey:product.productIdentifier]; 63 | [self trackTransaction:transaction forProduct:product]; 64 | [self.transactions removeObjectForKey:product.productIdentifier]; 65 | [self.productRequests removeObjectForKey:product.productIdentifier]; 66 | } 67 | } 68 | } 69 | 70 | #pragma mark - Track 71 | - (void)trackTransaction:(SKPaymentTransaction *)transaction forProduct:(SKProduct *)product 72 | { 73 | // it seems the identifier is nil for renewable subscriptions 74 | // see http://stackoverflow.com/questions/14827059/skpaymenttransactions-originaltransaction-transactionreceipt-nil-for-restore-on 75 | // there isn't a spec'd event for this case ( https://segment.com/docs/spec/ecommerce/v2/ ) so ignoring it for now 76 | if (transaction.transactionIdentifier == nil) { 77 | return; 78 | } 79 | 80 | NSString *currency = [product.priceLocale objectForKey:NSLocaleCurrencyCode]; 81 | 82 | [self.analytics track:@"Order Completed" properties:@{ 83 | @"orderId" : transaction.transactionIdentifier, 84 | @"affiliation" : @"App Store", 85 | @"currency" : currency ?: @"", 86 | @"products" : @[ 87 | @{ 88 | @"sku" : transaction.transactionIdentifier, 89 | @"quantity" : @(transaction.payment.quantity), 90 | @"productId" : product.productIdentifier ?: @"", 91 | @"price" : product.price ?: @0, 92 | @"name" : product.localizedTitle ?: @"", 93 | } 94 | ] 95 | }]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGUserDefaultsStorage.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGUserDefaultsStorage.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 8/24/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SEGStorage.h" 11 | 12 | 13 | @interface SEGUserDefaultsStorage : NSObject 14 | 15 | @property (nonatomic, strong, nullable) id crypto; 16 | @property (nonnull, nonatomic, readonly) NSUserDefaults *defaults; 17 | @property (nullable, nonatomic, readonly) NSString *namespacePrefix; 18 | 19 | - (instancetype _Nonnull)initWithDefaults:(NSUserDefaults *_Nonnull)defaults namespacePrefix:(NSString *_Nullable)namespacePrefix crypto:(id _Nullable)crypto; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGUserDefaultsStorage.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGUserDefaultsStorage.m 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 8/24/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import "SEGUtils.h" 10 | #import "SEGUserDefaultsStorage.h" 11 | #import "SEGCrypto.h" 12 | 13 | 14 | @implementation SEGUserDefaultsStorage 15 | 16 | - (instancetype)initWithDefaults:(NSUserDefaults *)defaults namespacePrefix:(NSString *)namespacePrefix crypto:(id)crypto 17 | { 18 | if (self = [super init]) { 19 | _defaults = defaults; 20 | _namespacePrefix = namespacePrefix; 21 | _crypto = crypto; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)removeKey:(NSString *)key 27 | { 28 | [self.defaults removeObjectForKey:[self namespacedKey:key]]; 29 | } 30 | 31 | - (void)resetAll 32 | { 33 | // Courtesy of http://stackoverflow.com/questions/6358737/nsuserdefaults-reset 34 | if (!self.namespacePrefix) { 35 | NSString *domainName = [[NSBundle mainBundle] bundleIdentifier]; 36 | if (domainName) { 37 | [self.defaults removePersistentDomainForName:domainName]; 38 | return; 39 | } 40 | } 41 | for (NSString *key in self.defaults.dictionaryRepresentation.allKeys) { 42 | if (!self.namespacePrefix || [key hasPrefix:self.namespacePrefix]) { 43 | [self.defaults removeObjectForKey:key]; 44 | } 45 | } 46 | [self.defaults synchronize]; 47 | } 48 | 49 | - (void)setData:(NSData *)data forKey:(NSString *)key 50 | { 51 | key = [self namespacedKey:key]; 52 | if (!self.crypto) { 53 | [self.defaults setObject:data forKey:key]; 54 | return; 55 | } 56 | NSData *encryptedData = [self.crypto encrypt:data]; 57 | [self.defaults setObject:encryptedData forKey:key]; 58 | } 59 | 60 | - (NSData *)dataForKey:(NSString *)key 61 | { 62 | key = [self namespacedKey:key]; 63 | if (!self.crypto) { 64 | return [self.defaults objectForKey:key]; 65 | } 66 | NSData *data = [self.defaults objectForKey:key]; 67 | if (!data) { 68 | SEGLog(@"WARNING: No data file for key %@", key); 69 | return nil; 70 | } 71 | return [self.crypto decrypt:data]; 72 | } 73 | 74 | - (NSDictionary *)dictionaryForKey:(NSString *)key 75 | { 76 | if (!self.crypto) { 77 | key = [self namespacedKey:key]; 78 | return [self.defaults dictionaryForKey:key]; 79 | } 80 | return [self plistForKey:key]; 81 | } 82 | 83 | - (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key 84 | { 85 | if (!self.crypto) { 86 | key = [self namespacedKey:key]; 87 | [self.defaults setObject:dictionary forKey:key]; 88 | return; 89 | } 90 | [self setPlist:dictionary forKey:key]; 91 | } 92 | 93 | - (NSArray *)arrayForKey:(NSString *)key 94 | { 95 | if (!self.crypto) { 96 | key = [self namespacedKey:key]; 97 | return [self.defaults arrayForKey:key]; 98 | } 99 | return [self plistForKey:key]; 100 | } 101 | 102 | - (void)setArray:(NSArray *)array forKey:(NSString *)key 103 | { 104 | if (!self.crypto) { 105 | key = [self namespacedKey:key]; 106 | [self.defaults setObject:array forKey:key]; 107 | return; 108 | } 109 | [self setPlist:array forKey:key]; 110 | } 111 | 112 | - (NSString *)stringForKey:(NSString *)key 113 | { 114 | if (!self.crypto) { 115 | key = [self namespacedKey:key]; 116 | return [self.defaults stringForKey:key]; 117 | } 118 | return [self plistForKey:key]; 119 | } 120 | 121 | - (void)setString:(NSString *)string forKey:(NSString *)key 122 | { 123 | if (!self.crypto) { 124 | key = [self namespacedKey:key]; 125 | [self.defaults setObject:string forKey:key]; 126 | return; 127 | } 128 | [self setPlist:string forKey:key]; 129 | } 130 | 131 | #pragma mark - Helpers 132 | 133 | - (id _Nullable)plistForKey:(NSString *)key 134 | { 135 | NSData *data = [self dataForKey:key]; 136 | return data ? [SEGUtils plistFromData:data] : nil; 137 | } 138 | 139 | - (void)setPlist:(id _Nonnull)plist forKey:(NSString *)key 140 | { 141 | NSData *data = [SEGUtils dataFromPlist:plist]; 142 | if (data) { 143 | [self setData:data forKey:key]; 144 | } 145 | } 146 | 147 | - (NSString *)namespacedKey:(NSString *)key 148 | { 149 | if (self.namespacePrefix) { 150 | return [NSString stringWithFormat:@"%@.%@", self.namespacePrefix, key]; 151 | } 152 | return key; 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGUtils.h 3 | // 4 | // 5 | 6 | #import 7 | #import "SEGAnalyticsUtils.h" 8 | 9 | 10 | @interface SEGUtils : NSObject 11 | 12 | + (NSData *_Nullable)dataFromPlist:(nonnull id)plist; 13 | + (id _Nullable)plistFromData:(NSData *_Nonnull)data; 14 | 15 | + (id _Nullable)traverseJSON:(id _Nullable)object andReplaceWithFilters:(nonnull NSDictionary*)patterns; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/SEGUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGUtils.m 3 | // 4 | // 5 | 6 | #import "SEGUtils.h" 7 | 8 | 9 | @implementation SEGUtils 10 | 11 | + (NSData *_Nullable)dataFromPlist:(nonnull id)plist 12 | { 13 | NSError *error = nil; 14 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist 15 | format:NSPropertyListXMLFormat_v1_0 16 | options:0 17 | error:&error]; 18 | if (error) { 19 | SEGLog(@"Unable to serialize data from plist object", error, plist); 20 | } 21 | return data; 22 | } 23 | 24 | + (id _Nullable)plistFromData:(NSData *_Nonnull)data 25 | { 26 | NSError *error = nil; 27 | id plist = [NSPropertyListSerialization propertyListWithData:data 28 | options:0 29 | format:nil 30 | error:&error]; 31 | if (error) { 32 | SEGLog(@"Unable to parse plist from data %@", error); 33 | } 34 | return plist; 35 | } 36 | 37 | 38 | +(id)traverseJSON:(id)object andReplaceWithFilters:(NSDictionary*)patterns 39 | { 40 | if (object == nil || object == NSNull.null || [object isKindOfClass:NSNull.class]) { 41 | return object; 42 | } 43 | 44 | if ([object isKindOfClass:NSDictionary.class]) { 45 | NSDictionary* dict = object; 46 | NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:dict.count]; 47 | 48 | for (NSString* key in dict.allKeys) { 49 | newDict[key] = [self traverseJSON:dict[key] andReplaceWithFilters:patterns]; 50 | } 51 | 52 | return newDict; 53 | } 54 | 55 | if ([object isKindOfClass:NSArray.class]) { 56 | NSArray* array = object; 57 | NSMutableArray* newArray = [NSMutableArray arrayWithCapacity:array.count]; 58 | 59 | for (int i = 0; i < array.count; i++) { 60 | newArray[i] = [self traverseJSON:array[i] andReplaceWithFilters:patterns]; 61 | } 62 | 63 | return newArray; 64 | } 65 | 66 | if ([object isKindOfClass:NSString.class]) { 67 | NSError* error = nil; 68 | NSMutableString* str = [object mutableCopy]; 69 | 70 | for (NSString* pattern in patterns) { 71 | NSRegularExpression* re = [NSRegularExpression regularExpressionWithPattern:pattern 72 | options:0 73 | error:&error]; 74 | 75 | if (error) { 76 | @throw error; 77 | } 78 | 79 | NSInteger matches = [re replaceMatchesInString:str 80 | options:0 81 | range:NSMakeRange(0, str.length) 82 | withTemplate:patterns[pattern]]; 83 | 84 | if (matches > 0) { 85 | SEGLog(@"%@ Redacted value from action: %@", self, pattern); 86 | } 87 | } 88 | 89 | return str; 90 | } 91 | 92 | return object; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/UIViewController+SEGScreen.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface UIViewController (SEGScreen) 5 | 6 | + (void)seg_swizzleViewDidAppear; 7 | + (UIViewController *)seg_topViewController; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Internal/UIViewController+SEGScreen.m: -------------------------------------------------------------------------------- 1 | #import "UIViewController+SEGScreen.h" 2 | #import 3 | #import "SEGAnalytics.h" 4 | #import "SEGAnalyticsUtils.h" 5 | 6 | 7 | @implementation UIViewController (SEGScreen) 8 | 9 | + (void)seg_swizzleViewDidAppear 10 | { 11 | static dispatch_once_t onceToken; 12 | dispatch_once(&onceToken, ^{ 13 | Class class = [self class]; 14 | 15 | SEL originalSelector = @selector(viewDidAppear:); 16 | SEL swizzledSelector = @selector(seg_viewDidAppear:); 17 | 18 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 19 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 20 | 21 | BOOL didAddMethod = 22 | class_addMethod(class, 23 | originalSelector, 24 | method_getImplementation(swizzledMethod), 25 | method_getTypeEncoding(swizzledMethod)); 26 | 27 | if (didAddMethod) { 28 | class_replaceMethod(class, 29 | swizzledSelector, 30 | method_getImplementation(originalMethod), 31 | method_getTypeEncoding(originalMethod)); 32 | } else { 33 | method_exchangeImplementations(originalMethod, swizzledMethod); 34 | } 35 | }); 36 | } 37 | 38 | 39 | + (UIViewController *)seg_topViewController 40 | { 41 | UIViewController *root = [[SEGAnalytics sharedAnalytics] configuration].application.delegate.window.rootViewController; 42 | return [self seg_topViewController:root]; 43 | } 44 | 45 | + (UIViewController *)seg_topViewController:(UIViewController *)rootViewController 46 | { 47 | UIViewController *presentedViewController = rootViewController.presentedViewController; 48 | if (presentedViewController != nil) { 49 | return [self seg_topViewController:presentedViewController]; 50 | } 51 | 52 | if ([rootViewController isKindOfClass:[UINavigationController class]]) { 53 | UIViewController *lastViewController = [[(UINavigationController *)rootViewController viewControllers] lastObject]; 54 | return [self seg_topViewController:lastViewController]; 55 | } 56 | 57 | return rootViewController; 58 | } 59 | 60 | - (void)seg_viewDidAppear:(BOOL)animated 61 | { 62 | UIViewController *top = [[self class] seg_topViewController]; 63 | if (!top) { 64 | SEGLog(@"Could not infer screen."); 65 | return; 66 | } 67 | 68 | NSString *name = [top title]; 69 | if (!name || name.length == 0) { 70 | name = [[[top class] description] stringByReplacingOccurrencesOfString:@"ViewController" withString:@""]; 71 | // Class name could be just "ViewController". 72 | if (name.length == 0) { 73 | SEGLog(@"Could not infer screen name."); 74 | name = @"Unknown"; 75 | } 76 | } 77 | [[SEGAnalytics sharedAnalytics] screen:name properties:nil options:nil]; 78 | 79 | [self seg_viewDidAppear:animated]; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Middlewares/SEGContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGContext.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/19/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SEGIntegration.h" 11 | 12 | typedef NS_ENUM(NSInteger, SEGEventType) { 13 | // Should not happen, but default state 14 | SEGEventTypeUndefined, 15 | // Core Tracking Methods 16 | SEGEventTypeIdentify, 17 | SEGEventTypeTrack, 18 | SEGEventTypeScreen, 19 | SEGEventTypeGroup, 20 | SEGEventTypeAlias, 21 | 22 | // General utility 23 | SEGEventTypeReset, 24 | SEGEventTypeFlush, 25 | 26 | // Remote Notification 27 | SEGEventTypeReceivedRemoteNotification, 28 | SEGEventTypeFailedToRegisterForRemoteNotifications, 29 | SEGEventTypeRegisteredForRemoteNotifications, 30 | SEGEventTypeHandleActionWithForRemoteNotification, 31 | 32 | // Application Lifecycle 33 | SEGEventTypeApplicationLifecycle, 34 | // DidFinishLaunching, 35 | // SEGEventTypeApplicationDidEnterBackground, 36 | // SEGEventTypeApplicationWillEnterForeground, 37 | // SEGEventTypeApplicationWillTerminate, 38 | // SEGEventTypeApplicationWillResignActive, 39 | // SEGEventTypeApplicationDidBecomeActive, 40 | 41 | // Misc. 42 | SEGEventTypeContinueUserActivity, 43 | SEGEventTypeOpenURL, 44 | 45 | }; 46 | 47 | @class SEGAnalytics; 48 | @protocol SEGMutableContext; 49 | 50 | 51 | @interface SEGContext : NSObject 52 | 53 | // Loopback reference to the top level SEGAnalytics object. 54 | // Not sure if it's a good idea to keep this around in the context. 55 | // since we don't really want people to use it due to the circular 56 | // reference and logic (Thus prefixing with underscore). But 57 | // Right now it is required for integrations to work so I guess we'll leave it in. 58 | @property (nonatomic, readonly, nonnull) SEGAnalytics *_analytics; 59 | @property (nonatomic, readonly) SEGEventType eventType; 60 | 61 | @property (nonatomic, readonly, nullable) NSString *userId; 62 | @property (nonatomic, readonly, nullable) NSString *anonymousId; 63 | @property (nonatomic, readonly, nullable) NSError *error; 64 | @property (nonatomic, readonly, nullable) SEGPayload *payload; 65 | @property (nonatomic, readonly) BOOL debug; 66 | 67 | - (instancetype _Nonnull)initWithAnalytics:(SEGAnalytics *_Nonnull)analytics; 68 | 69 | - (SEGContext *_Nonnull)modify:(void (^_Nonnull)(id _Nonnull ctx))modify; 70 | 71 | @end 72 | 73 | @protocol SEGMutableContext 74 | 75 | @property (nonatomic) SEGEventType eventType; 76 | @property (nonatomic, nullable) NSString *userId; 77 | @property (nonatomic, nullable) NSString *anonymousId; 78 | @property (nonatomic, nullable) SEGPayload *payload; 79 | @property (nonatomic, nullable) NSError *error; 80 | @property (nonatomic) BOOL debug; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Middlewares/SEGContext.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGContext.m 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/19/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import "SEGContext.h" 10 | 11 | 12 | @interface SEGContext () 13 | 14 | @property (nonatomic) SEGEventType eventType; 15 | @property (nonatomic, nullable) NSString *userId; 16 | @property (nonatomic, nullable) NSString *anonymousId; 17 | @property (nonatomic, nullable) SEGPayload *payload; 18 | @property (nonatomic, nullable) NSError *error; 19 | @property (nonatomic) BOOL debug; 20 | 21 | @end 22 | 23 | 24 | @implementation SEGContext 25 | 26 | - (instancetype)init 27 | { 28 | @throw [NSException exceptionWithName:@"Bad Initization" 29 | reason:@"Please use initWithAnalytics:" 30 | userInfo:nil]; 31 | } 32 | 33 | - (instancetype)initWithAnalytics:(SEGAnalytics *)analytics 34 | { 35 | if (self = [super init]) { 36 | __analytics = analytics; 37 | // TODO: Have some other way of indicating the debug flag is on too. 38 | // Also, for logging it'd be damn nice to implement a logging protocol 39 | // such as CocoalumberJack and allow developers to pipe logs to wherever they want 40 | // Of course we wouldn't us depend on it. it'd be like a soft dependency where 41 | // analytics-ios would totally work without it but works even better with it! 42 | #ifdef DEBUG 43 | _debug = YES; 44 | #endif 45 | } 46 | return self; 47 | } 48 | 49 | - (SEGContext *_Nonnull)modify:(void (^_Nonnull)(id _Nonnull ctx))modify 50 | { 51 | // We're also being a bit clever here by implementing SEGContext actually as a mutable 52 | // object but hiding that implementation detail from consumer of the API. 53 | // In production also instead of copying self we simply just return self 54 | // because the net effect is the same anyways. In the end we get a lot of the benefits 55 | // of immutable data structure without the cost of having to allocate and reallocate 56 | // objects over and over again. 57 | SEGContext *context = self.debug ? [self copy] : self; 58 | modify(context); 59 | // TODO: We could probably add some validation here that the newly modified context 60 | // is actualy valid. For example, `eventType` should match `paylaod` class. 61 | // or anonymousId should never be null. 62 | return context; 63 | } 64 | 65 | #pragma mark - NSCopying 66 | 67 | - (id)copyWithZone:(NSZone *)zone 68 | { 69 | SEGContext *ctx = [[SEGContext allocWithZone:zone] initWithAnalytics:self._analytics]; 70 | ctx.eventType = self.eventType; 71 | ctx.userId = self.userId; 72 | ctx.anonymousId = self.anonymousId; 73 | ctx.payload = self.payload; 74 | ctx.error = self.error; 75 | ctx.debug = self.debug; 76 | return ctx; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Middlewares/SEGMiddleware.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGMiddleware.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/19/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SEGContext.h" 11 | 12 | typedef void (^SEGMiddlewareNext)(SEGContext *_Nullable newContext); 13 | 14 | @protocol SEGMiddleware 15 | @required 16 | 17 | // NOTE: If you want to hold onto references of context AFTER passing it through to the next 18 | // middleware, you should explicitly create a copy via `[context copy]` to guarantee 19 | // that it does not get changed from underneath you because contexts can be implemented 20 | // as mutable objects under the hood for performance optimization. 21 | // The behavior of keeping reference to a context AFTER passing it to the next middleware 22 | // is strictly undefined. 23 | 24 | // Middleware should **always** call `next`. If the intention is to explicitly filter out 25 | // events from downstream, call `next` with `nil` as the param. 26 | // It's ok to save next callback until a more convenient time, but it should always always be done. 27 | // We'll probably actually add tests to sure it is so. 28 | // TODO: Should we add error as second param to next? 29 | - (void)context:(SEGContext *_Nonnull)context next:(SEGMiddlewareNext _Nonnull)next; 30 | 31 | @end 32 | 33 | typedef void (^SEGMiddlewareBlock)(SEGContext *_Nonnull context, SEGMiddlewareNext _Nonnull next); 34 | 35 | 36 | @interface SEGBlockMiddleware : NSObject 37 | 38 | @property (nonnull, nonatomic, readonly) SEGMiddlewareBlock block; 39 | 40 | - (instancetype _Nonnull)initWithBlock:(SEGMiddlewareBlock _Nonnull)block; 41 | 42 | @end 43 | 44 | 45 | typedef void (^RunMiddlewaresCallback)(BOOL earlyExit, NSArray> *_Nonnull remainingMiddlewares); 46 | 47 | // XXX TODO: Add some tests for SEGMiddlewareRunner 48 | @interface SEGMiddlewareRunner : NSObject 49 | 50 | // While it is certainly technically possible to change middlewares dynamically on the fly. we're explicitly NOT 51 | // gonna support that for now to keep things simple. If there is a real need later we'll see then. 52 | @property (nonnull, nonatomic, readonly) NSArray> *middlewares; 53 | 54 | - (void)run:(SEGContext *_Nonnull)context callback:(RunMiddlewaresCallback _Nullable)callback; 55 | 56 | - (instancetype _Nonnull)initWithMiddlewares:(NSArray> *_Nonnull)middlewares; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/Middlewares/SEGMiddleware.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGMiddleware.m 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/19/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import "SEGUtils.h" 10 | #import "SEGMiddleware.h" 11 | 12 | 13 | @implementation SEGBlockMiddleware 14 | 15 | - (instancetype)initWithBlock:(SEGMiddlewareBlock)block 16 | { 17 | if (self = [super init]) { 18 | _block = block; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)context:(SEGContext *)context next:(SEGMiddlewareNext)next 24 | { 25 | self.block(context, next); 26 | } 27 | 28 | @end 29 | 30 | 31 | @implementation SEGMiddlewareRunner 32 | 33 | - (instancetype)initWithMiddlewares:(NSArray> *_Nonnull)middlewares 34 | { 35 | if (self = [super init]) { 36 | _middlewares = middlewares; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)run:(SEGContext *_Nonnull)context callback:(RunMiddlewaresCallback _Nullable)callback 42 | { 43 | [self runMiddlewares:self.middlewares context:context callback:callback]; 44 | } 45 | 46 | // TODO: Maybe rename SEGContext to SEGEvent to be a bit more clear? 47 | // We could also use some sanity check / other types of logging here. 48 | - (void)runMiddlewares:(NSArray> *_Nonnull)middlewares 49 | context:(SEGContext *_Nonnull)context 50 | callback:(RunMiddlewaresCallback _Nullable)callback 51 | { 52 | BOOL earlyExit = context == nil; 53 | if (middlewares.count == 0 || earlyExit) { 54 | if (callback) { 55 | callback(earlyExit, middlewares); 56 | } 57 | return; 58 | } 59 | 60 | [middlewares[0] context:context next:^(SEGContext *_Nullable newContext) { 61 | NSArray *remainingMiddlewares = [middlewares subarrayWithRange:NSMakeRange(1, middlewares.count - 1)]; 62 | [self runMiddlewares:remainingMiddlewares context:newContext callback:callback]; 63 | }]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/SEGAnalytics.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SEGIntegrationFactory.h" 3 | #import "SEGCrypto.h" 4 | #import "SEGAnalyticsConfiguration.h" 5 | #import "SEGSerializableValue.h" 6 | 7 | NS_ASSUME_NONNULL_BEGIN 8 | 9 | /** 10 | * This object provides an API for recording analytics. 11 | */ 12 | @class SEGAnalyticsConfiguration; 13 | 14 | 15 | @interface SEGAnalytics : NSObject 16 | 17 | /** 18 | * Used by the analytics client to configure various options. 19 | */ 20 | @property (nonatomic, strong, readonly) SEGAnalyticsConfiguration *configuration; 21 | 22 | /** 23 | * Setup this analytics client instance. 24 | * 25 | * @param configuration The configuration used to setup the client. 26 | */ 27 | - (instancetype)initWithConfiguration:(SEGAnalyticsConfiguration *)configuration; 28 | 29 | /** 30 | * Setup the analytics client. 31 | * 32 | * @param configuration The configuration used to setup the client. 33 | */ 34 | + (void)setupWithConfiguration:(SEGAnalyticsConfiguration *)configuration; 35 | 36 | /** 37 | * Enabled/disables debug logging to trace your data going through the SDK. 38 | * 39 | * @param showDebugLogs `YES` to enable logging, `NO` otherwise. `NO` by default. 40 | */ 41 | + (void)debug:(BOOL)showDebugLogs; 42 | 43 | /** 44 | * Returns the shared analytics client. 45 | * 46 | * @see -setupWithConfiguration: 47 | */ 48 | + (instancetype)sharedAnalytics; 49 | 50 | /*! 51 | @method 52 | 53 | @abstract 54 | Associate a user with their unique ID and record traits about them. 55 | 56 | @param userId A database ID (or email address) for this user. If you don't have a userId 57 | but want to record traits, you should pass nil. For more information on how we 58 | generate the UUID and Apple's policies on IDs, see https://segment.io/libraries/ios#ids 59 | 60 | @param traits A dictionary of traits you know about the user. Things like: email, name, plan, etc. 61 | 62 | @param options A dictionary of options, such as the `@"anonymousId"` key. If no anonymous ID is specified one will be generated for you. 63 | 64 | @discussion 65 | When you learn more about who your user is, you can record that information with identify. 66 | 67 | */ 68 | - (void)identify:(NSString *_Nullable)userId traits:(SERIALIZABLE_DICT _Nullable)traits options:(SERIALIZABLE_DICT _Nullable)options; 69 | - (void)identify:(NSString *_Nullable)userId traits:(SERIALIZABLE_DICT _Nullable)traits; 70 | - (void)identify:(NSString *_Nullable)userId; 71 | 72 | 73 | /*! 74 | @method 75 | 76 | @abstract 77 | Record the actions your users perform. 78 | 79 | @param event The name of the event you're tracking. We recommend using human-readable names 80 | like `Played a Song` or `Updated Status`. 81 | 82 | @param properties A dictionary of properties for the event. If the event was 'Added to Shopping Cart', it might 83 | have properties like price, productType, etc. 84 | 85 | @discussion 86 | When a user performs an action in your app, you'll want to track that action for later analysis. Use the event name to say what the user did, and properties to specify any interesting details of the action. 87 | 88 | */ 89 | - (void)track:(NSString *)event properties:(SERIALIZABLE_DICT _Nullable)properties options:(SERIALIZABLE_DICT _Nullable)options; 90 | - (void)track:(NSString *)event properties:(SERIALIZABLE_DICT _Nullable)properties; 91 | - (void)track:(NSString *)event; 92 | 93 | /*! 94 | @method 95 | 96 | @abstract 97 | Record the screens or views your users see. 98 | 99 | @param screenTitle The title of the screen being viewed. We recommend using human-readable names 100 | like 'Photo Feed' or 'Completed Purchase Screen'. 101 | 102 | @param properties A dictionary of properties for the screen view event. If the event was 'Added to Shopping Cart', 103 | it might have properties like price, productType, etc. 104 | 105 | @discussion 106 | When a user views a screen in your app, you'll want to record that here. For some tools like Google Analytics and Flurry, screen views are treated specially, and are different from "events" kind of like "page views" on the web. For services that don't treat "screen views" specially, we map "screen" straight to "track" with the same parameters. For example, Mixpanel doesn't treat "screen views" any differently. So a call to "screen" will be tracked as a normal event in Mixpanel, but get sent to Google Analytics and Flurry as a "screen". 107 | 108 | */ 109 | - (void)screen:(NSString *)screenTitle properties:(SERIALIZABLE_DICT _Nullable)properties options:(SERIALIZABLE_DICT _Nullable)options; 110 | - (void)screen:(NSString *)screenTitle properties:(SERIALIZABLE_DICT _Nullable)properties; 111 | - (void)screen:(NSString *)screenTitle; 112 | 113 | /*! 114 | @method 115 | 116 | @abstract 117 | Associate a user with a group, organization, company, project, or w/e *you* call them. 118 | 119 | @param groupId A database ID for this group. 120 | @param traits A dictionary of traits you know about the group. Things like: name, employees, etc. 121 | 122 | @discussion 123 | When you learn more about who the group is, you can record that information with group. 124 | 125 | */ 126 | - (void)group:(NSString *)groupId traits:(SERIALIZABLE_DICT _Nullable)traits options:(SERIALIZABLE_DICT _Nullable)options; 127 | - (void)group:(NSString *)groupId traits:(SERIALIZABLE_DICT _Nullable)traits; 128 | - (void)group:(NSString *)groupId; 129 | 130 | /*! 131 | @method 132 | 133 | @abstract 134 | Merge two user identities, effectively connecting two sets of user data as one. 135 | This may not be supported by all integrations. 136 | 137 | @param newId The new ID you want to alias the existing ID to. The existing ID will be either the 138 | previousId if you have called identify, or the anonymous ID. 139 | 140 | @discussion 141 | When you learn more about who the group is, you can record that information with group. 142 | 143 | */ 144 | - (void)alias:(NSString *)newId options:(SERIALIZABLE_DICT _Nullable)options; 145 | - (void)alias:(NSString *)newId; 146 | 147 | // todo: docs 148 | - (void)receivedRemoteNotification:(NSDictionary *)userInfo; 149 | - (void)failedToRegisterForRemoteNotificationsWithError:(NSError *)error; 150 | - (void)registeredForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; 151 | - (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo; 152 | - (void)continueUserActivity:(NSUserActivity *)activity; 153 | - (void)openURL:(NSURL *)url options:(NSDictionary *)options; 154 | 155 | /*! 156 | @method 157 | 158 | @abstract 159 | Trigger an upload of all queued events. 160 | 161 | @discussion 162 | This is useful when you want to force all messages queued on the device to be uploaded. Please note that not all integrations 163 | respond to this method. 164 | */ 165 | - (void)flush; 166 | 167 | /*! 168 | @method 169 | 170 | @abstract 171 | Reset any user state that is cached on the device. 172 | 173 | @discussion 174 | This is useful when a user logs out and you want to clear the identity. It will clear any 175 | traits or userId's cached on the device. 176 | */ 177 | - (void)reset; 178 | 179 | /*! 180 | @method 181 | 182 | @abstract 183 | Enable the sending of analytics data. Enabled by default. 184 | 185 | @discussion 186 | Occasionally used in conjunction with disable user opt-out handling. 187 | */ 188 | - (void)enable; 189 | 190 | 191 | /*! 192 | @method 193 | 194 | @abstract 195 | Completely disable the sending of any analytics data. 196 | 197 | @discussion 198 | If have a way for users to actively or passively (sometimes based on location) opt-out of 199 | analytics data collection, you can use this method to turn off all data collection. 200 | */ 201 | - (void)disable; 202 | 203 | 204 | /** 205 | * Version of the library. 206 | */ 207 | + (NSString *)version; 208 | 209 | /** 210 | * Returns a dictionary of integrations that are bundled. This is an internal Segment API, and may be removed at any time 211 | * without notice. 212 | */ 213 | - (NSDictionary *)bundledIntegrations; 214 | 215 | /** Returns the anonymous ID of the current user. */ 216 | - (NSString *)getAnonymousId; 217 | 218 | /** Returns the configuration used to create the analytics client. */ 219 | - (SEGAnalyticsConfiguration *)configuration; 220 | 221 | 222 | @end 223 | 224 | NS_ASSUME_NONNULL_END 225 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/SEGAnalyticsConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGIntegrationsManager.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/20/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol SEGApplicationProtocol 13 | @property (nullable, nonatomic, assign) id delegate; 14 | - (UIBackgroundTaskIdentifier)seg_beginBackgroundTaskWithName:(nullable NSString *)taskName expirationHandler:(void (^__nullable)(void))handler; 15 | - (void)seg_endBackgroundTask:(UIBackgroundTaskIdentifier)identifier; 16 | @end 17 | 18 | 19 | @interface UIApplication (SEGApplicationProtocol) 20 | @end 21 | 22 | typedef NSMutableURLRequest *_Nonnull (^SEGRequestFactory)(NSURL *_Nonnull); 23 | 24 | @protocol SEGIntegrationFactory; 25 | @protocol SEGCrypto; 26 | @protocol SEGMiddleware; 27 | 28 | /** 29 | * This object provides a set of properties to control various policies of the analytics client. Other than `writeKey`, these properties can be changed at any time. 30 | */ 31 | @interface SEGAnalyticsConfiguration : NSObject 32 | 33 | /** 34 | * Creates and returns a configuration with default settings and the given write key. 35 | * 36 | * @param writeKey Your project's write key from segment.io. 37 | */ 38 | + (_Nonnull instancetype)configurationWithWriteKey:(NSString *_Nonnull)writeKey; 39 | 40 | /** 41 | * Your project's write key from segment.io. 42 | * 43 | * @see +configurationWithWriteKey: 44 | */ 45 | @property (nonatomic, copy, readonly, nonnull) NSString *writeKey; 46 | 47 | /** 48 | * Whether the analytics client should use location services. 49 | * If `YES` and the host app hasn't asked for permission to use location services then the user will be presented with an alert view asking to do so. `NO` by default. 50 | * If `YES`, please make sure to add a description for `NSLocationAlwaysUsageDescription` in your `Info.plist` explaining why your app is accessing Location APIs. 51 | */ 52 | @property (nonatomic, assign) BOOL shouldUseLocationServices; 53 | 54 | /** 55 | * Whether the analytics client should track advertisting info. `YES` by default. 56 | */ 57 | @property (nonatomic, assign) BOOL enableAdvertisingTracking; 58 | 59 | /** 60 | * The number of queued events that the analytics client should flush at. Setting this to `1` will not queue any events and will use more battery. `20` by default. 61 | */ 62 | @property (nonatomic, assign) NSUInteger flushAt; 63 | 64 | 65 | /** 66 | * Whether the analytics client should automatically make a track call for application lifecycle events, such as "Application Installed", "Application Updated" and "Application Opened". 67 | */ 68 | @property (nonatomic, assign) BOOL trackApplicationLifecycleEvents; 69 | 70 | 71 | /** 72 | * Whether the analytics client should record bluetooth information. If `YES`, please make sure to add a description for `NSBluetoothPeripheralUsageDescription` in your `Info.plist` explaining explaining why your app is accessing Bluetooth APIs. `NO` by default. 73 | */ 74 | @property (nonatomic, assign) BOOL shouldUseBluetooth; 75 | 76 | /** 77 | * Whether the analytics client should automatically make a screen call when a view controller is added to a view hierarchy. Because the underlying implementation uses method swizzling, we recommend initializing the analytics client as early as possible (before any screens are displayed), ideally during the Application delegate's applicationDidFinishLaunching method. 78 | */ 79 | @property (nonatomic, assign) BOOL recordScreenViews; 80 | 81 | /** 82 | * Whether the analytics client should automatically track in-app purchases from the App Store. 83 | */ 84 | @property (nonatomic, assign) BOOL trackInAppPurchases; 85 | 86 | /** 87 | * Whether the analytics client should automatically track push notifications. 88 | */ 89 | @property (nonatomic, assign) BOOL trackPushNotifications; 90 | 91 | /** 92 | * Whether the analytics client should automatically track deep links. You'll still need to call the continueUserActivity and openURL methods on the analytics client. 93 | */ 94 | @property (nonatomic, assign) BOOL trackDeepLinks; 95 | 96 | /** 97 | * Whether the analytics client should automatically track attribution data from enabled providers using the mobile service. 98 | */ 99 | @property (nonatomic, assign) BOOL trackAttributionData; 100 | 101 | /** 102 | * Dictionary indicating the options the app was launched with. 103 | */ 104 | @property (nonatomic, strong, nullable) NSDictionary *launchOptions; 105 | 106 | /** 107 | * Set a custom request factory. 108 | */ 109 | @property (nonatomic, strong, nullable) SEGRequestFactory requestFactory; 110 | 111 | /** 112 | * Set a custom crypto 113 | */ 114 | @property (nonatomic, strong, nullable) id crypto; 115 | 116 | /** 117 | * Set custom middlewares. Will be run before all integrations 118 | */ 119 | @property (nonatomic, strong, nullable) NSArray> *middlewares; 120 | 121 | /** 122 | * Register a factory that can be used to create an integration. 123 | */ 124 | - (void)use:(id _Nonnull)factory; 125 | 126 | /** 127 | * Leave this nil for iOS extensions, otherwise set to UIApplication.sharedApplication. 128 | */ 129 | @property (nonatomic, strong, nullable) id application; 130 | 131 | /** 132 | * A dictionary of filters to redact payloads before they are sent. 133 | * This is an experimental feature that currently only applies to Deep Links. 134 | * It is subject to change to allow for more flexible customizations in the future. 135 | * 136 | * The key of this dictionary should be a regular expression string pattern, 137 | * and the value should be a regular expression substitution template. 138 | * 139 | * By default, this contains a Facebook auth token filter, configured as such: 140 | * @code 141 | * @"(fb\\d+://authorize#access_token=)([^ ]+)": @"$1((redacted/fb-auth-token))" 142 | * @endcode 143 | * 144 | * This will replace any matching occurences to a redacted version: 145 | * @code 146 | * "fb123456789://authorize#access_token=secretsecretsecretsecret&some=data" 147 | * @endcode 148 | * 149 | * Becomes: 150 | * @code 151 | * "fb123456789://authorize#access_token=((redacted/fb-auth-token))" 152 | * @endcode 153 | * 154 | */ 155 | @property (nonatomic, strong, nonnull) NSDictionary* payloadFilters; 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/SEGAnalyticsConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEGIntegrationsManager.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 9/20/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import "SEGAnalyticsConfiguration.h" 10 | #import "SEGCrypto.h" 11 | 12 | 13 | @implementation UIApplication (SEGApplicationProtocol) 14 | 15 | - (UIBackgroundTaskIdentifier)seg_beginBackgroundTaskWithName:(nullable NSString *)taskName expirationHandler:(void (^__nullable)(void))handler 16 | { 17 | return [self beginBackgroundTaskWithName:taskName expirationHandler:handler]; 18 | } 19 | 20 | - (void)seg_endBackgroundTask:(UIBackgroundTaskIdentifier)identifier 21 | { 22 | [self endBackgroundTask:identifier]; 23 | } 24 | 25 | @end 26 | 27 | 28 | @interface SEGAnalyticsConfiguration () 29 | 30 | @property (nonatomic, copy, readwrite) NSString *writeKey; 31 | @property (nonatomic, strong, readonly) NSMutableArray *factories; 32 | 33 | @end 34 | 35 | 36 | @implementation SEGAnalyticsConfiguration 37 | 38 | + (instancetype)configurationWithWriteKey:(NSString *)writeKey 39 | { 40 | return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey]; 41 | } 42 | 43 | - (instancetype)initWithWriteKey:(NSString *)writeKey 44 | { 45 | if (self = [self init]) { 46 | self.writeKey = writeKey; 47 | } 48 | return self; 49 | } 50 | 51 | - (instancetype)init 52 | { 53 | if (self = [super init]) { 54 | self.shouldUseLocationServices = NO; 55 | self.enableAdvertisingTracking = YES; 56 | self.shouldUseBluetooth = NO; 57 | self.flushAt = 20; 58 | self.payloadFilters = @{ 59 | @"(fb\\d+://authorize#access_token=)([^ ]+)": @"$1((redacted/fb-auth-token))" 60 | }; 61 | _factories = [NSMutableArray array]; 62 | Class applicationClass = NSClassFromString(@"UIApplication"); 63 | if (applicationClass) { 64 | #pragma clang diagnostic push 65 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 66 | _application = [applicationClass performSelector:NSSelectorFromString(@"sharedApplication")]; 67 | #pragma clang diagnostic pop 68 | } 69 | } 70 | return self; 71 | } 72 | 73 | - (void)use:(id)factory 74 | { 75 | [self.factories addObject:factory]; 76 | } 77 | 78 | - (NSString *)description 79 | { 80 | return [NSString stringWithFormat:@"<%p:%@, %@>", self, self.class, [self dictionaryWithValuesForKeys:@[ @"writeKey", @"shouldUseLocationServices", @"flushAt" ]]]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Classes/SEGSerializableValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEGSerializableValue.h 3 | // Analytics 4 | // 5 | // Created by Tony Xiao on 11/29/16. 6 | // Copyright © 2016 Segment. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /* 12 | Acceptable dictionary values are 13 | NSString (String); 14 | NSNumber (Int, Float, Bool); 15 | NSNull 16 | NSDate => ISO8601 String 17 | NSURL => absoluteURL String 18 | NSArray of the above 19 | NSDictionary of the above 20 | */ 21 | #define SERIALIZABLE_DICT NSDictionary * 22 | 23 | /* 24 | Acceptable dictionary values are 25 | NSString (String); 26 | NSNumber (Int, Float, Bool); 27 | NSNull 28 | NSArray of the above 29 | NSDictionary of the above 30 | */ 31 | #define JSON_DICT NSDictionary * 32 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/Analytics/Vendor/SEGReachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Tony Million. 3 | All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 12 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 15 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 16 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 17 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 19 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 20 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 21 | POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #import 25 | #import 26 | 27 | /** 28 | * Does ARC support GCD objects? 29 | * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+ 30 | * 31 | * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h 32 | **/ 33 | #if OS_OBJECT_USE_OBJC 34 | #define NEEDS_DISPATCH_RETAIN_RELEASE 0 35 | #else 36 | #define NEEDS_DISPATCH_RETAIN_RELEASE 1 37 | #endif 38 | 39 | /** 40 | * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X. 41 | * 42 | * @see http://nshipster.com/ns_enum-ns_options/ 43 | **/ 44 | #ifndef NS_ENUM 45 | #define NS_ENUM(_type, _name) \ 46 | enum _name : _type _name; \ 47 | enum _name : _type 48 | #endif 49 | 50 | NS_ASSUME_NONNULL_BEGIN 51 | 52 | extern NSString *const kSEGReachabilityChangedNotification; 53 | 54 | typedef NS_ENUM(NSInteger, SEGNetworkStatus) { 55 | // Apple NetworkStatus Compatible Names. 56 | SEGNotReachable = 0, 57 | SEGReachableViaWiFi = 2, 58 | SEGReachableViaWWAN = 1 59 | }; 60 | 61 | @class SEGReachability; 62 | 63 | typedef void (^SEGNetworkReachable)(SEGReachability *reachability); 64 | typedef void (^SEGNetworkUnreachable)(SEGReachability *reachability); 65 | 66 | 67 | @interface SEGReachability : NSObject 68 | 69 | @property (nonatomic, copy, nullable) SEGNetworkReachable reachableBlock; 70 | @property (nonatomic, copy, nullable) SEGNetworkUnreachable unreachableBlock; 71 | 72 | 73 | @property (nonatomic, assign) BOOL reachableOnWWAN; 74 | 75 | + (SEGReachability *_Nullable)reachabilityWithHostname:(NSString *)hostname; 76 | + (SEGReachability *_Nullable)reachabilityForInternetConnection; 77 | + (SEGReachability *_Nullable)reachabilityForLocalWiFi; 78 | 79 | - (SEGReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref; 80 | 81 | - (BOOL)startNotifier; 82 | - (void)stopNotifier; 83 | 84 | - (BOOL)isReachable; 85 | - (BOOL)isReachableViaWWAN; 86 | - (BOOL)isReachableViaWiFi; 87 | 88 | // WWAN may be available, but not active until a connection has been established. 89 | // WiFi may require a connection for VPN on Demand. 90 | - (BOOL)isConnectionRequired; // Identical DDG variant. 91 | - (BOOL)connectionRequired; // Apple's routine. 92 | // Dynamic, on demand connection? 93 | - (BOOL)isConnectionOnDemand; 94 | // Is user intervention required? 95 | - (BOOL)isInterventionRequired; 96 | 97 | - (SEGNetworkStatus)currentReachabilityStatus; 98 | - (SCNetworkReachabilityFlags)reachabilityFlags; 99 | - (NSString *)currentReachabilityString; 100 | - (NSString *)currentReachabilityFlags; 101 | 102 | @end 103 | 104 | NS_ASSUME_NONNULL_END 105 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Segment.io, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Analytics/README.md: -------------------------------------------------------------------------------- 1 | # Analytics 2 | [![Circle CI](https://circleci.com/gh/segmentio/analytics-ios.svg?style=shield&circle-token=31c5b3e5edeb404b30141ead9dcef3eb37d16d4d)](https://circleci.com/gh/segmentio/analytics-ios) 3 | [![Version](https://img.shields.io/cocoapods/v/Analytics.svg?style=flat)](https://cocoapods.org//pods/Analytics) 4 | [![License](https://img.shields.io/cocoapods/l/Analytics.svg?style=flat)](http://cocoapods.org/pods/Analytics) 5 | [![codecov](https://codecov.io/gh/segmentio/analytics-ios/branch/master/graph/badge.svg)](https://codecov.io/gh/segmentio/analytics-ios) 6 | 7 | analytics-ios is an iOS client for Segment. 8 | 9 | Special thanks to [Tony Xiao](https://github.com/tonyxiao), [Lee Hasiuk](https://github.com/lhasiuk) and [Cristian Bica](https://github.com/cristianbica) for their contributions to the library! 10 | 11 | ## Installation 12 | 13 | Analytics is available through [CocoaPods](http://cocoapods.org) and [Carthage](https://github.com/Carthage/Carthage). 14 | 15 | ### CocoaPods 16 | 17 | ```ruby 18 | pod "Analytics", "3.6.0" 19 | ``` 20 | 21 | ### Carthage 22 | 23 | ``` 24 | github "segmentio/analytics-ios" 25 | ``` 26 | 27 | ## Quickstart 28 | 29 | Refer to the Quickstart documentation at [https://segment.com/docs/libraries/ios/quickstart](https://segment.com/docs/libraries/ios/quickstart/). 30 | 31 | ## Documentation 32 | 33 | More detailed documentation is available at [https://segment.com/docs/libraries/ios](https://segment.com/docs/libraries/ios/). 34 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Analytics (3.6.10) 3 | 4 | DEPENDENCIES: 5 | - Analytics (= 3.6.10) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - Analytics 10 | 11 | SPEC CHECKSUMS: 12 | Analytics: 63744ad4afa65c3bcdcdb7a94b62515bde5b3900 13 | 14 | PODFILE CHECKSUM: 1078e7b12a3b5e456d114d57af4de4aca03fb029 15 | 16 | COCOAPODS: 1.6.0 17 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.6.10 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Analytics : NSObject 3 | @end 4 | @implementation PodsDummy_Analytics 5 | @end 6 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "SEGAES256Crypto.h" 14 | #import "SEGCrypto.h" 15 | #import "SEGAliasPayload.h" 16 | #import "SEGGroupPayload.h" 17 | #import "SEGIdentifyPayload.h" 18 | #import "SEGIntegration.h" 19 | #import "SEGIntegrationFactory.h" 20 | #import "SEGIntegrationsManager.h" 21 | #import "SEGPayload.h" 22 | #import "SEGScreenPayload.h" 23 | #import "SEGTrackPayload.h" 24 | #import "NSData+SEGGZIP.h" 25 | #import "SEGAnalyticsUtils.h" 26 | #import "SEGFileStorage.h" 27 | #import "SEGHTTPClient.h" 28 | #import "SEGSegmentIntegration.h" 29 | #import "SEGSegmentIntegrationFactory.h" 30 | #import "SEGStorage.h" 31 | #import "SEGStoreKitTracker.h" 32 | #import "SEGUserDefaultsStorage.h" 33 | #import "SEGUtils.h" 34 | #import "UIViewController+SEGScreen.h" 35 | #import "SEGContext.h" 36 | #import "SEGMiddleware.h" 37 | #import "SEGAnalytics.h" 38 | #import "SEGAnalyticsConfiguration.h" 39 | #import "SEGSerializableValue.h" 40 | #import "SEGReachability.h" 41 | 42 | FOUNDATION_EXPORT double AnalyticsVersionNumber; 43 | FOUNDATION_EXPORT const unsigned char AnalyticsVersionString[]; 44 | 45 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics.modulemap: -------------------------------------------------------------------------------- 1 | framework module Analytics { 2 | umbrella header "Analytics-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Analytics/Analytics.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Analytics 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT} 6 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Analytics 7 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 8 | SKIP_INSTALL = YES 9 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Analytics 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2016 Segment.io, Inc. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2016 Segment.io, Inc. 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | Analytics 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_AnalyticsSwiftExample : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_AnalyticsSwiftExample 5 | @end 6 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Signs a framework with the provided identity 113 | code_sign_if_enabled() { 114 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 115 | # Use the current code_sign_identity 116 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 117 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 118 | 119 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 120 | code_sign_cmd="$code_sign_cmd &" 121 | fi 122 | echo "$code_sign_cmd" 123 | eval "$code_sign_cmd" 124 | fi 125 | } 126 | 127 | # Strip invalid architectures 128 | strip_invalid_archs() { 129 | binary="$1" 130 | # Get architectures for current target binary 131 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 132 | # Intersect them with the architectures we are building for 133 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 134 | # If there are no archs supported by this binary then warn the user 135 | if [[ -z "$intersected_archs" ]]; then 136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 137 | STRIP_BINARY_RETVAL=0 138 | return 139 | fi 140 | stripped="" 141 | for arch in $binary_archs; do 142 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 143 | # Strip non-valid architectures in-place 144 | lipo -remove "$arch" -output "$binary" "$binary" 145 | stripped="$stripped $arch" 146 | fi 147 | done 148 | if [[ "$stripped" ]]; then 149 | echo "Stripped $binary of architectures:$stripped" 150 | fi 151 | STRIP_BINARY_RETVAL=1 152 | } 153 | 154 | 155 | if [[ "$CONFIGURATION" == "Debug" ]]; then 156 | install_framework "${BUILT_PRODUCTS_DIR}/Analytics/Analytics.framework" 157 | fi 158 | if [[ "$CONFIGURATION" == "Release" ]]; then 159 | install_framework "${BUILT_PRODUCTS_DIR}/Analytics/Analytics.framework" 160 | fi 161 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 162 | wait 163 | fi 164 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_AnalyticsSwiftExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_AnalyticsSwiftExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Analytics" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Analytics/Analytics.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Analytics" -framework "Security" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_AnalyticsSwiftExample { 2 | umbrella header "Pods-AnalyticsSwiftExample-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /AnalyticsSwiftExample/Pods/Target Support Files/Pods-AnalyticsSwiftExample/Pods-AnalyticsSwiftExample.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Analytics" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Analytics/Analytics.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Analytics" -framework "Security" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /AnalyticsSwiftTests/AnalyticsSwiftTests.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Cocoa 24 | import XCTest 25 | import AnalyticsSwift 26 | 27 | final class AnalyticsSwiftTests: XCTestCase { 28 | func testExample() { 29 | let analytics = Analytics(writeKey: "Z2qQi0HsunlFVULJmUi6R0JAwIF2S7R1", queue: Array(), 30 | executor: SynchronousExecutor(name: "com.segment.executor.test")) 31 | for index in 1...21 { 32 | analytics.enqueue(messageBuilder: TrackMessageBuilder(event: "hello, world" + String(index)).userId("prateek")) 33 | analytics.enqueue(messageBuilder: TrackMessageBuilder(event: "bye, world" + String(index)).userId("prateek")) 34 | } 35 | print("Sent messages to client.") 36 | analytics.flush() 37 | print("Triggered explicit flush.") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /AnalyticsSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /AnalyticsSwiftTests/SynchronousExecutor.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright © 2015 Segment, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | import AnalyticsSwift 25 | 26 | /** An Executor implementation that runs tasks and blocks until they complete. */ 27 | 28 | public class SynchronousExecutor: Executor { 29 | private let dispatcher: DispatchQueue 30 | 31 | init(name: String) { 32 | self.dispatcher = DispatchQueue(label: name) 33 | } 34 | 35 | public func submit(task: @escaping () -> ()) { 36 | dispatcher.sync(execute: task) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Segment, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Targets. 3 | # 4 | 5 | clean: 6 | @xcodebuild clean 7 | 8 | test: 9 | @xctool -scheme AnalyticsSwift test 10 | 11 | build: 12 | @xcodebuild 13 | 14 | # 15 | # Phonies. 16 | # 17 | 18 | .PHONY: clean 19 | .PHONY: test 20 | .PHONY: build 21 | 22 | --------------------------------------------------------------------------------