├── .gitignore
├── LICENSE.md
├── README.md
└── source
├── GoogleAnalytics
├── GoogleAnalytics.swift
└── GoogleAnalyticsHelper.swift
├── GoogleAnalyticsSwiftDemo
├── GoogleAnalyticsSwiftDemo.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
└── GoogleAnalyticsSwiftDemo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Base.lproj
│ └── MainMenu.xib
│ ├── Info.plist
│ ├── MainWindowController.swift
│ └── MainWindowController.xib
└── html
└── gapp.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 atjason
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Note:**
2 |
3 | For Swift 5, Xcode 11.3 & Catalina Support and updated README, please check the 'swift5' branch. Thanks [revblaze](https://github.com/revblaze)'s conribution.
4 |
5 | Google Analytics for Mac OS X using Swift.
6 |
7 |
8 | # Introduction
9 | As we know, Google Analytics is perfect for user data collection and analytics. But unfortunately, there's no official SDK for Mac OS X.
10 |
11 | But still, there're several ways to use Google Analytics on Mac OS X. MacGoogleAnalytics may be what you found.
12 |
13 | Overall, there're 2 parts work together to use Google Analytics.
14 |
15 | - HTML File
16 | - This is the real place to send data to Google Analytics server. It uses Google Analytics's standard JavaScript API.
17 |
18 | - Mac OS X Part
19 | - In Mac OS X side, it loads the html file just mentioned. Send data like event to the html file as parameters. And then these data will be send in the html file through the JavaScript API.
20 |
21 | # HTML File
22 | You can find the html file in `source > html > gapp.html`.
23 |
24 | ## Parameter of HTML File
25 | The parameter of the html file is exactly the same with normal html file.
26 |
27 | ```html
28 | /gapp.html?param1=paramValue1¶m2=paramValue2&...
29 | ```
30 |
31 | Demo of the html file with full parameters:
32 |
33 | ```html
34 | /gapp.html?id=UA-12345678-1&name=ihosts&version=1.1.0&ca=menu&ev=click&la=rate
35 | ```
36 |
37 | Here are the supported parameter list.
38 |
39 | - `id`
40 | - The Google Analytics id, e.g., 'UA-12345678-1'.
41 | - **Note**: When you create a Google Analytics id, use the '**Mobile app**', but not 'Website'.
42 |
43 | 
44 |
45 | - `name`
46 | - The product name, e.g., 'ihosts' for [iHosts](http://ex.toolinbox.net/ga/url.html?utm_medium=ihosts&utm_source=GitHub&id=UA-26569268-10&url=https%3a%2f%2fitunes.apple.com%2fapp%2fid1102004240%3fls%3d1%26mt%3d12). You can track different products using same Google Analytics id, even I don't suggest you do that.
47 | - `version`
48 | - The app's version, e.g., '1.2.1'.
49 | - `ca` (*optional*)
50 | - The Google Analytics event category name.
51 | - `ev` (*optional*)
52 | - The Google Analytics event action name.
53 | - `la` (*optional*)
54 | - The Google Analytics event label value.
55 |
56 | **Note**: The full url include the parameters will be encoded. So no need to worry about the characters are valid or not. They will all be valid.
57 |
58 | ## Deploy the HTML File
59 | The html file should be deployed to a domain before it can be accessed. I deployed it at GitHub with my domain, you can feel free to use it.
60 |
61 | ```html
62 | http://atjason.com/MacGoogleAnalytics/gapp.html
63 | ```
64 |
65 | You can also deploy it to your server with your domain.
66 |
67 | **Tip**: you may want to reduce the html file size by compressing the JavaScript code in it. Thus it will be faster to download.
68 |
69 | # Mac OS X Part
70 | There's only 2 files needed for Mac OS X part: `GoogleAnalytics.swift` and `GoogleAnalytics.swift`.
71 |
72 | ## GoogleAnalytics.swift
73 | Good news is, you even don't need to touch this file.
74 |
75 | This is the key file to integrate with Google Analytics. If you read the code, you will find it in fact loads the html file in a WebView, and combine the data as parameters for the html.
76 |
77 | ## GoogleAnalyticsHelper.swift
78 | This class help you easily use Google Analytics.
79 |
80 | Don't worry, you only need to update these properties:
81 |
82 | - `GAID`
83 | - The Google Analytics id, e.g., 'UA-12345678-1'. This is the 'id' used as parameter of html file.
84 | - `GAName`
85 | - The product name, e.g., 'ihosts' for **TODO iHosts Link**. This is the 'name' used as parameter of html file.
86 | - `PingURL`
87 | - The full url of gapp.html, e.g., 'http://atjason.com/MacGoogleAnalytics/gapp.html' mentioned above, or the url you deployed.
88 | - `PingTimeInterval`
89 | - By default it's 6h. It means telling Google Analytics every 6h that 'hey, I'm alive'. This will help Google Analytics to calculate the active users.
90 |
91 | # Demo Project Using Swift
92 | How to integrate MacGoogleAnalytics in your project? Just put the `GoogleAnalytics.swift` and `GoogleAnalyticsHelper.swift` in your project.
93 |
94 | Then, you need to start Google Analytics when app finished launching, stop it when app will terminate.
95 |
96 | ```swift
97 | func applicationDidFinishLaunching(aNotification: NSNotification) {
98 | GAHelper.startGoogleAnalytics()
99 | }
100 |
101 | func applicationWillTerminate(notification: NSNotification) {
102 | GAHelper.stopGoogleAnalytics()
103 | }
104 | ```
105 |
106 | The main usage of Google Analytics is to send event. How to send the event? Just as simple as following code.
107 |
108 | ```swift
109 | GA.sendEvent("menu", event: "click", label: "rate")
110 | ```
111 |
112 | That's all.
113 |
114 | ## Work With HTTP
115 | When run your project with MacGoogleAnalytics, you may meet this error:
116 |
117 | *App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.*
118 |
119 | Apple disable http access by default for security reason. If you really need to use http, could manually allow it. Open `Info.plist`, and add the following lines:
120 |
121 | ```swift
122 | NSAppTransportSecurity
123 |
124 | NSAllowsArbitraryLoads
125 |
126 |
127 | ```
128 |
129 | Please keep in mind that this solution should be your last resort. You’d ideally want to enable HTTPS on your service, or next, whitelist domains on which you want HTTP and if none of this is possible, then use this approach.
130 |
131 | # Demo Project Using Objective C
132 | TODO
133 |
134 | # View Data in Google Analytics Site
135 | After data are collected, you can view them in Google Analytics as normal: [https://analytics.google.com/analytics/web/](https://analytics.google.com/analytics/web/). During debug, you can also check whether it works or not in 'Real-Time'.
136 |
137 | Just give a tip for OS X version. In the demo app using swift, it collect event of `options > option_a > yes or no`. You may want to know, how many users enable this option in OS X 10.10 and how many for 10.11. How to do it?
138 |
139 | Open the `Behavior > Events > Top Events`, select `options > option_a`. Here you should be able to see all the events. Then add a new custom Segment, in `Technology > Operating System Version`, add *contains* '10.10', save it as 'OS X 10.10'. Add another Segment for 'OS X 10.11'.
140 |
141 | 
142 |
143 | And then, apply the Segments, it could distinguish the data of OS X 10.10 or 10.11.
144 |
145 | 
146 |
147 | # Release on Mac App Store
148 | Yes, this way could be released on Mac App Store. In fact, these of my apps have already released on Mac App Store.
149 |
150 | - [iHosts](http://ex.toolinbox.net/ga/url.html?utm_medium=ihosts&utm_source=GitHub&id=UA-26569268-10&url=https%3a%2f%2fitunes.apple.com%2fapp%2fid1102004240%3fls%3d1%26mt%3d12)
151 | - [Daily Clipboard](http://ex.toolinbox.net/ga/url.html?utm_medium=clip&utm_source=GitHub&id=UA-26569268-10&url=https%3a%2f%2fitunes.apple.com%2fapp%2fid1056935452%3fls%3d1%26mt%3d12)
152 | - [Attention Timer](http://ex.toolinbox.net/ga/url.html?utm_medium=timer&utm_source=GitHub&id=UA-26569268-10&url=https%3a%2f%2fitunes.apple.com%2fapp%2fid1062139745%3fls%3d1%26mt%3d12)
153 |
154 | ## Show Your Product
155 | If your product using MacGoogleAnalytics and you'd like to share it here, pull requests welcome :)
156 |
157 | # License
158 | MacGoogleAnalytics is licensed under the terms of the MIT license. Just feel free to use it :)
159 |
160 | # Donate
161 | If you think Mac Google Analytics is helpful for you, welcome to [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=quietjason%40gmail%2ecom&lc=US&item_name=Mac%20Google%20Analytics&button_subtype=services¤cy_code=USD&bn=PP%2dBuyNowBF%3abtn_buynow_SM%2egif%3aNonHosted) :)
162 |
163 |
164 |
--------------------------------------------------------------------------------
/source/GoogleAnalytics/GoogleAnalytics.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAnalytics.swift
3 | // Test
4 | //
5 | // Created by Jason Zheng on 11/27/15.
6 | // Copyright © 2015 Jason Zheng. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import WebKit
11 |
12 | let GA = GoogleAnalytics.sharedInstance
13 |
14 | class GoogleAnalytics: NSObject {
15 |
16 | static let sharedInstance = GoogleAnalytics()
17 | private override init() { super.init() }
18 |
19 | private var webViews = Set()
20 | private let timeIntervalDelayToRemoveWebView = NSTimeInterval(10)
21 | private weak var timer: NSTimer?
22 |
23 | var yes = "yes"
24 | var no = "no"
25 | var none = "none"
26 |
27 | private var pingURL = ""
28 | private let userUUIDKey = "User UUID"
29 | private let paramCategory = "ca"
30 | private let paramEvent = "ev"
31 | private let paramLabel = "la"
32 |
33 | func setPingURL(url: String) {
34 | pingURL = url
35 | }
36 |
37 | func startPing(timeInternal: NSTimeInterval) {
38 | if timer == nil {
39 | timer = NSTimer.scheduledTimerWithTimeInterval(
40 | timeInternal, target: self, selector: #selector(GoogleAnalytics.doPing),
41 | userInfo: nil, repeats: true)
42 | timer?.tolerance = timeInternal * 0.1
43 | }
44 |
45 | timer?.fire()
46 | }
47 |
48 | func stopPing() {
49 | timer?.invalidate()
50 | }
51 |
52 | func doPing() {
53 | GA.sendEvent("app", event: "ping", label: GAHelper.getAppVersion())
54 | }
55 |
56 | func doPingURL(url: String) {
57 |
58 | let encodedURL = url.stringByAddingPercentEncodingWithAllowedCharacters(
59 | NSCharacterSet.URLQueryAllowedCharacterSet())
60 |
61 | if encodedURL != nil {
62 | if let requestURL = NSURL(string: encodedURL!) {
63 | dispatch_async(dispatch_get_main_queue(), {
64 | let newWebView = WebView()
65 | self.webViews.insert(newWebView)
66 | newWebView.frameLoadDelegate = self
67 |
68 | let request = NSURLRequest(URL: requestURL)
69 | newWebView.mainFrame.loadRequest(request)
70 | })
71 |
72 | } else {
73 | NSLog("Failed to generate NSURL from \(url)")
74 | }
75 |
76 | } else {
77 | NSLog("Failed to encode URL from \(url)")
78 | }
79 | }
80 |
81 | func sendEvent(category: String, event: String?, label: String?) {
82 | var url = pingURL
83 |
84 | if !category.isEmpty {
85 | if !url.containsString("?") {
86 | url += "?"
87 | } else {
88 | url += "&"
89 | }
90 |
91 | url += "\(paramCategory)=\(category)"
92 |
93 | if (event != nil) && (!event!.isEmpty) {
94 | url += "&\(paramEvent)=\(event!)"
95 |
96 | if (label != nil) && (!label!.isEmpty) {
97 | url += "&\(paramLabel)=\(label!)"
98 | }
99 | }
100 | }
101 |
102 | self.performSelector(#selector(GoogleAnalytics.doPingURL(_:)), withObject: url)
103 | }
104 |
105 | func initUUIDIfNeeded() {
106 | let userDefaults = NSUserDefaults.standardUserDefaults()
107 |
108 | let userUUID = userDefaults.valueForKey(userUUIDKey) as? String
109 |
110 | if userUUID == nil {
111 | userDefaults.setValue(NSUUID().UUIDString, forKey: userUUIDKey)
112 |
113 | let locale = NSLocale.currentLocale().objectForKey(NSLocaleIdentifier) as? String ?? ""
114 | let os = GAHelper.getOSXVersion()
115 | let version = GAHelper.getAppVersion()
116 | let info = "\(locale)_\(os)_\(version)"
117 | GA.sendEvent("app", event: "init", label: info)
118 | }
119 | }
120 | }
121 |
122 | extension GoogleAnalytics: WebFrameLoadDelegate {
123 |
124 | func webView(sender: WebView!, didFinishLoadForFrame frame: WebFrame!) {
125 | self.performSelector(#selector(GoogleAnalytics.removeUnusedWebView(_:)),
126 | withObject: sender,
127 | afterDelay: timeIntervalDelayToRemoveWebView)
128 | }
129 |
130 | func removeUnusedWebView(webView: WebView) {
131 | webView.close()
132 | self.webViews.remove(webView)
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/source/GoogleAnalytics/GoogleAnalyticsHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAnalytics.swift
3 | // Test
4 | //
5 | // Created by Jason Zheng on 11/27/15.
6 | // Copyright © 2015 Jason Zheng. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | typealias GAHelper = GoogleAnalyticsHelper
12 |
13 | class GoogleAnalyticsHelper {
14 |
15 | static let GAID = "UA-76794534-4"
16 | static let GAName = "Demo"
17 | static let PingURL = "http://atjason.com/MacGoogleAnalytics/gapp.html?id=\(GAID)&name=\(GAName)&version=\(GoogleAnalyticsHelper.getAppVersion())"
18 | static let PingTimeInterval: NSTimeInterval = 3600 * 6 // Ping every 6h
19 |
20 | // MARK: - Helper
21 |
22 | static func startGoogleAnalytics() {
23 | GA.setPingURL(PingURL)
24 |
25 | GA.startPing(PingTimeInterval)
26 | GA.sendEvent("app", event: "start", label: getAppVersion())
27 | GA.initUUIDIfNeeded()
28 | }
29 |
30 | static func stopGoogleAnalytics() {
31 | GA.stopPing()
32 | }
33 |
34 | static func getAppVersion() -> String {
35 | let versionObject = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"]
36 | let version = versionObject as? String ?? "0.0.0"
37 |
38 | return version
39 | }
40 |
41 | static func getOSXVersion() -> String {
42 | let version = NSProcessInfo.processInfo().operatingSystemVersion
43 | let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
44 | return versionString
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 96806FA91CE5BA930012CF72 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96806FA81CE5BA930012CF72 /* AppDelegate.swift */; };
11 | 96806FAB1CE5BA930012CF72 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96806FAA1CE5BA930012CF72 /* Assets.xcassets */; };
12 | 96806FAE1CE5BA930012CF72 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96806FAC1CE5BA930012CF72 /* MainMenu.xib */; };
13 | 96806FB71CE5BABD0012CF72 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96806FB51CE5BABD0012CF72 /* MainWindowController.swift */; };
14 | 96806FB81CE5BABD0012CF72 /* MainWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96806FB61CE5BABD0012CF72 /* MainWindowController.xib */; };
15 | 96806FBB1CE5BB7D0012CF72 /* GoogleAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96806FBA1CE5BB7D0012CF72 /* GoogleAnalytics.swift */; };
16 | 96806FBD1CE5BE930012CF72 /* GoogleAnalyticsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96806FBC1CE5BE930012CF72 /* GoogleAnalyticsHelper.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 96806FA51CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GoogleAnalyticsSwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 96806FA81CE5BA930012CF72 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
22 | 96806FAA1CE5BA930012CF72 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | 96806FAD1CE5BA930012CF72 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
24 | 96806FAF1CE5BA930012CF72 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
25 | 96806FB51CE5BABD0012CF72 /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; };
26 | 96806FB61CE5BABD0012CF72 /* MainWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainWindowController.xib; sourceTree = ""; };
27 | 96806FBA1CE5BB7D0012CF72 /* GoogleAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GoogleAnalytics.swift; path = ../../GoogleAnalytics/GoogleAnalytics.swift; sourceTree = ""; };
28 | 96806FBC1CE5BE930012CF72 /* GoogleAnalyticsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GoogleAnalyticsHelper.swift; path = ../../GoogleAnalytics/GoogleAnalyticsHelper.swift; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 96806FA21CE5BA930012CF72 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 96806F9C1CE5BA930012CF72 = {
43 | isa = PBXGroup;
44 | children = (
45 | 96806FA71CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo */,
46 | 96806FA61CE5BA930012CF72 /* Products */,
47 | );
48 | sourceTree = "";
49 | };
50 | 96806FA61CE5BA930012CF72 /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | 96806FA51CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | 96806FA71CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 96806FB91CE5BB6B0012CF72 /* GoogleAnalytics */,
62 | 96806FA81CE5BA930012CF72 /* AppDelegate.swift */,
63 | 96806FB51CE5BABD0012CF72 /* MainWindowController.swift */,
64 | 96806FB61CE5BABD0012CF72 /* MainWindowController.xib */,
65 | 96806FAA1CE5BA930012CF72 /* Assets.xcassets */,
66 | 96806FAC1CE5BA930012CF72 /* MainMenu.xib */,
67 | 96806FAF1CE5BA930012CF72 /* Info.plist */,
68 | );
69 | path = GoogleAnalyticsSwiftDemo;
70 | sourceTree = "";
71 | };
72 | 96806FB91CE5BB6B0012CF72 /* GoogleAnalytics */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 96806FBA1CE5BB7D0012CF72 /* GoogleAnalytics.swift */,
76 | 96806FBC1CE5BE930012CF72 /* GoogleAnalyticsHelper.swift */,
77 | );
78 | name = GoogleAnalytics;
79 | sourceTree = "";
80 | };
81 | /* End PBXGroup section */
82 |
83 | /* Begin PBXNativeTarget section */
84 | 96806FA41CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo */ = {
85 | isa = PBXNativeTarget;
86 | buildConfigurationList = 96806FB21CE5BA930012CF72 /* Build configuration list for PBXNativeTarget "GoogleAnalyticsSwiftDemo" */;
87 | buildPhases = (
88 | 96806FA11CE5BA930012CF72 /* Sources */,
89 | 96806FA21CE5BA930012CF72 /* Frameworks */,
90 | 96806FA31CE5BA930012CF72 /* Resources */,
91 | );
92 | buildRules = (
93 | );
94 | dependencies = (
95 | );
96 | name = GoogleAnalyticsSwiftDemo;
97 | productName = GoogleAnalyticsSwiftDemo;
98 | productReference = 96806FA51CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo.app */;
99 | productType = "com.apple.product-type.application";
100 | };
101 | /* End PBXNativeTarget section */
102 |
103 | /* Begin PBXProject section */
104 | 96806F9D1CE5BA930012CF72 /* Project object */ = {
105 | isa = PBXProject;
106 | attributes = {
107 | LastSwiftUpdateCheck = 0730;
108 | LastUpgradeCheck = 0730;
109 | ORGANIZATIONNAME = "Jason Zheng";
110 | TargetAttributes = {
111 | 96806FA41CE5BA930012CF72 = {
112 | CreatedOnToolsVersion = 7.3;
113 | };
114 | };
115 | };
116 | buildConfigurationList = 96806FA01CE5BA930012CF72 /* Build configuration list for PBXProject "GoogleAnalyticsSwiftDemo" */;
117 | compatibilityVersion = "Xcode 3.2";
118 | developmentRegion = English;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = 96806F9C1CE5BA930012CF72;
125 | productRefGroup = 96806FA61CE5BA930012CF72 /* Products */;
126 | projectDirPath = "";
127 | projectRoot = "";
128 | targets = (
129 | 96806FA41CE5BA930012CF72 /* GoogleAnalyticsSwiftDemo */,
130 | );
131 | };
132 | /* End PBXProject section */
133 |
134 | /* Begin PBXResourcesBuildPhase section */
135 | 96806FA31CE5BA930012CF72 /* Resources */ = {
136 | isa = PBXResourcesBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | 96806FAB1CE5BA930012CF72 /* Assets.xcassets in Resources */,
140 | 96806FB81CE5BABD0012CF72 /* MainWindowController.xib in Resources */,
141 | 96806FAE1CE5BA930012CF72 /* MainMenu.xib in Resources */,
142 | );
143 | runOnlyForDeploymentPostprocessing = 0;
144 | };
145 | /* End PBXResourcesBuildPhase section */
146 |
147 | /* Begin PBXSourcesBuildPhase section */
148 | 96806FA11CE5BA930012CF72 /* Sources */ = {
149 | isa = PBXSourcesBuildPhase;
150 | buildActionMask = 2147483647;
151 | files = (
152 | 96806FB71CE5BABD0012CF72 /* MainWindowController.swift in Sources */,
153 | 96806FA91CE5BA930012CF72 /* AppDelegate.swift in Sources */,
154 | 96806FBB1CE5BB7D0012CF72 /* GoogleAnalytics.swift in Sources */,
155 | 96806FBD1CE5BE930012CF72 /* GoogleAnalyticsHelper.swift in Sources */,
156 | );
157 | runOnlyForDeploymentPostprocessing = 0;
158 | };
159 | /* End PBXSourcesBuildPhase section */
160 |
161 | /* Begin PBXVariantGroup section */
162 | 96806FAC1CE5BA930012CF72 /* MainMenu.xib */ = {
163 | isa = PBXVariantGroup;
164 | children = (
165 | 96806FAD1CE5BA930012CF72 /* Base */,
166 | );
167 | name = MainMenu.xib;
168 | sourceTree = "";
169 | };
170 | /* End PBXVariantGroup section */
171 |
172 | /* Begin XCBuildConfiguration section */
173 | 96806FB01CE5BA930012CF72 /* Debug */ = {
174 | isa = XCBuildConfiguration;
175 | buildSettings = {
176 | ALWAYS_SEARCH_USER_PATHS = NO;
177 | CLANG_ANALYZER_NONNULL = YES;
178 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
179 | CLANG_CXX_LIBRARY = "libc++";
180 | CLANG_ENABLE_MODULES = YES;
181 | CLANG_ENABLE_OBJC_ARC = YES;
182 | CLANG_WARN_BOOL_CONVERSION = YES;
183 | CLANG_WARN_CONSTANT_CONVERSION = YES;
184 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
185 | CLANG_WARN_EMPTY_BODY = YES;
186 | CLANG_WARN_ENUM_CONVERSION = YES;
187 | CLANG_WARN_INT_CONVERSION = YES;
188 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
189 | CLANG_WARN_UNREACHABLE_CODE = YES;
190 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
191 | CODE_SIGN_IDENTITY = "-";
192 | COPY_PHASE_STRIP = NO;
193 | DEBUG_INFORMATION_FORMAT = dwarf;
194 | ENABLE_STRICT_OBJC_MSGSEND = YES;
195 | ENABLE_TESTABILITY = YES;
196 | GCC_C_LANGUAGE_STANDARD = gnu99;
197 | GCC_DYNAMIC_NO_PIC = NO;
198 | GCC_NO_COMMON_BLOCKS = YES;
199 | GCC_OPTIMIZATION_LEVEL = 0;
200 | GCC_PREPROCESSOR_DEFINITIONS = (
201 | "DEBUG=1",
202 | "$(inherited)",
203 | );
204 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
205 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
206 | GCC_WARN_UNDECLARED_SELECTOR = YES;
207 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
208 | GCC_WARN_UNUSED_FUNCTION = YES;
209 | GCC_WARN_UNUSED_VARIABLE = YES;
210 | MACOSX_DEPLOYMENT_TARGET = 10.10;
211 | MTL_ENABLE_DEBUG_INFO = YES;
212 | ONLY_ACTIVE_ARCH = YES;
213 | SDKROOT = macosx;
214 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
215 | };
216 | name = Debug;
217 | };
218 | 96806FB11CE5BA930012CF72 /* Release */ = {
219 | isa = XCBuildConfiguration;
220 | buildSettings = {
221 | ALWAYS_SEARCH_USER_PATHS = NO;
222 | CLANG_ANALYZER_NONNULL = YES;
223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
224 | CLANG_CXX_LIBRARY = "libc++";
225 | CLANG_ENABLE_MODULES = YES;
226 | CLANG_ENABLE_OBJC_ARC = YES;
227 | CLANG_WARN_BOOL_CONVERSION = YES;
228 | CLANG_WARN_CONSTANT_CONVERSION = YES;
229 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
230 | CLANG_WARN_EMPTY_BODY = YES;
231 | CLANG_WARN_ENUM_CONVERSION = YES;
232 | CLANG_WARN_INT_CONVERSION = YES;
233 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
234 | CLANG_WARN_UNREACHABLE_CODE = YES;
235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
236 | CODE_SIGN_IDENTITY = "-";
237 | COPY_PHASE_STRIP = NO;
238 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
239 | ENABLE_NS_ASSERTIONS = NO;
240 | ENABLE_STRICT_OBJC_MSGSEND = YES;
241 | GCC_C_LANGUAGE_STANDARD = gnu99;
242 | GCC_NO_COMMON_BLOCKS = YES;
243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
245 | GCC_WARN_UNDECLARED_SELECTOR = YES;
246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
247 | GCC_WARN_UNUSED_FUNCTION = YES;
248 | GCC_WARN_UNUSED_VARIABLE = YES;
249 | MACOSX_DEPLOYMENT_TARGET = 10.10;
250 | MTL_ENABLE_DEBUG_INFO = NO;
251 | SDKROOT = macosx;
252 | };
253 | name = Release;
254 | };
255 | 96806FB31CE5BA930012CF72 /* Debug */ = {
256 | isa = XCBuildConfiguration;
257 | buildSettings = {
258 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
259 | COMBINE_HIDPI_IMAGES = YES;
260 | INFOPLIST_FILE = GoogleAnalyticsSwiftDemo/Info.plist;
261 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
262 | PRODUCT_BUNDLE_IDENTIFIER = com.atjason.MacGoogleAnalytics.GoogleAnalyticsSwiftDemo;
263 | PRODUCT_NAME = "$(TARGET_NAME)";
264 | };
265 | name = Debug;
266 | };
267 | 96806FB41CE5BA930012CF72 /* Release */ = {
268 | isa = XCBuildConfiguration;
269 | buildSettings = {
270 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
271 | COMBINE_HIDPI_IMAGES = YES;
272 | INFOPLIST_FILE = GoogleAnalyticsSwiftDemo/Info.plist;
273 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
274 | PRODUCT_BUNDLE_IDENTIFIER = com.atjason.MacGoogleAnalytics.GoogleAnalyticsSwiftDemo;
275 | PRODUCT_NAME = "$(TARGET_NAME)";
276 | };
277 | name = Release;
278 | };
279 | /* End XCBuildConfiguration section */
280 |
281 | /* Begin XCConfigurationList section */
282 | 96806FA01CE5BA930012CF72 /* Build configuration list for PBXProject "GoogleAnalyticsSwiftDemo" */ = {
283 | isa = XCConfigurationList;
284 | buildConfigurations = (
285 | 96806FB01CE5BA930012CF72 /* Debug */,
286 | 96806FB11CE5BA930012CF72 /* Release */,
287 | );
288 | defaultConfigurationIsVisible = 0;
289 | defaultConfigurationName = Release;
290 | };
291 | 96806FB21CE5BA930012CF72 /* Build configuration list for PBXNativeTarget "GoogleAnalyticsSwiftDemo" */ = {
292 | isa = XCConfigurationList;
293 | buildConfigurations = (
294 | 96806FB31CE5BA930012CF72 /* Debug */,
295 | 96806FB41CE5BA930012CF72 /* Release */,
296 | );
297 | defaultConfigurationIsVisible = 0;
298 | defaultConfigurationName = Release;
299 | };
300 | /* End XCConfigurationList section */
301 | };
302 | rootObject = 96806F9D1CE5BA930012CF72 /* Project object */;
303 | }
304 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // GoogleAnalyticsSwiftDemo
4 | //
5 | // Created by Jason Zheng on 5/13/16.
6 | // Copyright © 2016 Jason Zheng. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | var mainWindowController: MainWindowController?
15 |
16 | func applicationDidFinishLaunching(aNotification: NSNotification) {
17 | let mainWindowController = MainWindowController()
18 | mainWindowController.showWindow(self)
19 |
20 | self.mainWindowController = mainWindowController
21 |
22 | GAHelper.startGoogleAnalytics()
23 | }
24 |
25 | func applicationWillTerminate(notification: NSNotification) {
26 | GAHelper.stopGoogleAnalytics()
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
666 |
667 |
668 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2016 Jason Zheng. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 | NSAppTransportSecurity
34 |
35 | NSAllowsArbitraryLoads
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/MainWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainWindowController.swift
3 | // GoogleAnalyticsSwiftDemo
4 | //
5 | // Created by Jason Zheng on 5/13/16.
6 | // Copyright © 2016 Jason Zheng. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class MainWindowController: NSWindowController {
12 |
13 | @IBOutlet weak var optionA: NSButton!
14 |
15 | override var windowNibName: String? {
16 | return "MainWindowController"
17 | }
18 |
19 | @IBAction func sendEvent(sender: NSButton!) {
20 | let selected = (optionA.state == NSOnState)
21 | let label = selected ? GA.yes : GA.no
22 | GA.sendEvent("options", event: "option_a", label: label)
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/source/GoogleAnalyticsSwiftDemo/GoogleAnalyticsSwiftDemo/MainWindowController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/source/html/gapp.html:
--------------------------------------------------------------------------------
1 |
54 |
55 |
56 |
--------------------------------------------------------------------------------