├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── Switcher.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Switcher.xcworkspace └── contents.xcworkspacedata ├── Switcher ├── AppDelegate.swift ├── AppStore.txt ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── SWAccountManager.swift ├── SWAppLoginManager.swift ├── SWEnterPasswordWindowController.swift ├── SWEnterPasswordWindowController.xib ├── SWMainPreferenceController.swift ├── SWMainPreferenceController.xib ├── SWMainTableCellView.swift ├── SWMainTableView.swift ├── SWMainViewController.swift ├── SWMainWindowController.swift ├── SWNewAccountWindowController.swift ├── SWNewAccountWindowController.xib ├── SWPreferences.swift └── iTunes.txt └── images └── 01.jpg /.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 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - type_name 4 | 5 | opt_in_rules: 6 | - empty_count 7 | - force_unwrapping 8 | 9 | function_body_length: 10 | warning: 50 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode9.2 2 | language: swift 3 | podfile: Podfile 4 | 5 | script: 6 | - set -o pipefail && xcodebuild -workspace Switcher.xcworkspace -scheme Switcher -sdk macosx10.13 | xcpretty 7 | before_install: 8 | - pod repo update 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Xinyu Zhao 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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'Switcher' do 5 | 6 | use_frameworks! 7 | 8 | pod 'MASPreferences' 9 | pod 'SwiftLint' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MASPreferences (1.3) 3 | - SwiftLint (0.25.0) 4 | 5 | DEPENDENCIES: 6 | - MASPreferences 7 | - SwiftLint 8 | 9 | SPEC CHECKSUMS: 10 | MASPreferences: c08b8622dd17b47da87669e741efd7c92e970e8c 11 | SwiftLint: e14651157288e9e01d6e1a71db7014fb5744a8ea 12 | 13 | PODFILE CHECKSUM: cb62383f509718584e823d2b85866ba9d32e3b55 14 | 15 | COCOAPODS: 1.3.1 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Switcher 2 | 3 | [![Build Status](https://travis-ci.org/X140Yu/Switcher.svg?branch=master)](https://travis-ci.org/X140Yu/Switcher) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | 6 | ## [中文说明](#chinese) 7 | 8 | Switcher is an OS X App which helps you easily switch Apple ID in App Store & iTunes App. 9 | 10 | If you use just one Apple ID, this app may not be that useful for you. But if you use multiple IDs at the same time and thought to switch between them hurt a lot, this may be the right app for you. 11 | 12 | ![](images/01.jpg) 13 | 14 | ## Release 15 | [Switcher v0.1](https://github.com/X140Yu/Switcher/releases/tag/v0.1) 16 | 17 | ## Usage 18 | 1. Enter `cmd + n` or press menu item `Account -> Add Account` to add an Apple ID. 19 | 2. Press `App Store` or `iTunes` button. 20 | 3. Enter the password for the Apple ID. 21 | 4. Press enter and wait patiently. 22 | 23 | The first time you use, it will request you to add itself to the Accessibility, feel free to add it, it causes nothing. 24 | 25 | PS. Right click a list item can choose to delete it. 26 | 27 | ## Is it safe? 28 | The app saves your IDs in a local file and every time you enter the password, for convenience, it will save it in the memory(NOT in a local file). So feel free to use this app, it's totally safe. 29 | 30 | ## Known issue 31 | Certain states of App Store and iTunes App may cause the AppleScript part run to error, but don't worry, click login again, it will work. 32 | 33 | 34 | # 中文说明 35 | Switcher 是一个用 Swift 编写的 OS X 应用,可以很轻松地切换 App Store 和 iTunes 的账号,对于同时使用多个 Apple ID 的人来说非常地方便。 36 | 37 | ## 应用下载 38 | [Switcher v0.1](https://github.com/X140Yu/Switcher/releases/tag/v0.1) 39 | 40 | ## 使用方法 41 | 1. 使用 `cmd + n` 快捷键或点击菜单栏的 `Account -> Add Account` 添加 Apple ID。 42 | 2. 点击条目上的 `App Store` 或 `iTunes` 按纽。 43 | 3. 在弹出的对话框中输入对应 ID 的密码。 44 | 4. 键入回车,然后耐心等待。 45 | 46 | 第一次使用本应用的时候会提示你把它加到 Accessibility 中,那是因为登录 App Store 或 iTunes 的时候使用了 AppleScript,放心加进去就好了。 47 | 48 | PS. 右键点击一个条目可以将它删掉。 49 | 50 | ## 安全吗? 51 | 为了使用的方便,应用会在本地储存已经添加的 Apple ID,而且会把密码储存在内存当中。本应用不会以任何方式储存用户的密码,请放心使用。 52 | 53 | ## 已知问题 54 | App Store 和 iTunes 应用的某些状态会导致 AppleScript 部分代码运行出错,不过再登录一次,问题就可以解决。 55 | 56 | 57 | -------------------------------------------------------------------------------- /Switcher.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 84755F5FF870492CD273BFFF /* Pods_Switcher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B7DCEC7B9DCEBE912153EB22 /* Pods_Switcher.framework */; }; 11 | A81312151D0316F000C40C4A /* SWMainTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81312141D0316F000C40C4A /* SWMainTableCellView.swift */; }; 12 | A81312171D03223200C40C4A /* SWMainTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81312161D03223200C40C4A /* SWMainTableView.swift */; }; 13 | A85CD37320690F4300406B5A /* AppStore.txt in Resources */ = {isa = PBXBuildFile; fileRef = A85CD37220690F4300406B5A /* AppStore.txt */; }; 14 | A85CD3752069EAC500406B5A /* iTunes.txt in Resources */ = {isa = PBXBuildFile; fileRef = A85CD3742069EAC500406B5A /* iTunes.txt */; }; 15 | A861CAEB1D2BFB5F00BA0BAC /* SWMainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A861CAEA1D2BFB5F00BA0BAC /* SWMainWindowController.swift */; }; 16 | A865212C1D09698B00C26D0F /* SWNewAccountWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A865212A1D09698B00C26D0F /* SWNewAccountWindowController.swift */; }; 17 | A865212D1D09698B00C26D0F /* SWNewAccountWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A865212B1D09698B00C26D0F /* SWNewAccountWindowController.xib */; }; 18 | A8C795051CF9317300FA3A7F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C795041CF9317300FA3A7F /* AppDelegate.swift */; }; 19 | A8C795071CF9317300FA3A7F /* SWMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C795061CF9317300FA3A7F /* SWMainViewController.swift */; }; 20 | A8C795091CF9317300FA3A7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A8C795081CF9317300FA3A7F /* Assets.xcassets */; }; 21 | A8C7950C1CF9317300FA3A7F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A8C7950A1CF9317300FA3A7F /* Main.storyboard */; }; 22 | A8EC4CE31D2BAAE30062C34D /* SWMainPreferenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EC4CE11D2BAAE30062C34D /* SWMainPreferenceController.swift */; }; 23 | A8EC4CE41D2BAAE30062C34D /* SWMainPreferenceController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A8EC4CE21D2BAAE30062C34D /* SWMainPreferenceController.xib */; }; 24 | A8EC4CE61D2BB29A0062C34D /* SWPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EC4CE51D2BB29A0062C34D /* SWPreferences.swift */; }; 25 | A8F7C9451D099322009422D2 /* SWAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7C9441D099322009422D2 /* SWAccountManager.swift */; }; 26 | A8F7C9471D09A072009422D2 /* SWAppLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7C9461D09A072009422D2 /* SWAppLoginManager.swift */; }; 27 | A8F7C94A1D09A5E2009422D2 /* SWEnterPasswordWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7C9481D09A5E2009422D2 /* SWEnterPasswordWindowController.swift */; }; 28 | A8F7C94B1D09A5E2009422D2 /* SWEnterPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A8F7C9491D09A5E2009422D2 /* SWEnterPasswordWindowController.xib */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 04DD167AA3688EA083AFAC09 /* Pods-Switcher.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switcher.release.xcconfig"; path = "Pods/Target Support Files/Pods-Switcher/Pods-Switcher.release.xcconfig"; sourceTree = ""; }; 33 | 295D74DF87F0F2F4F1347768 /* Pods-Switcher.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switcher.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Switcher/Pods-Switcher.debug.xcconfig"; sourceTree = ""; }; 34 | A81312141D0316F000C40C4A /* SWMainTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWMainTableCellView.swift; sourceTree = ""; }; 35 | A81312161D03223200C40C4A /* SWMainTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWMainTableView.swift; sourceTree = ""; }; 36 | A85CD37220690F4300406B5A /* AppStore.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = AppStore.txt; sourceTree = ""; }; 37 | A85CD3742069EAC500406B5A /* iTunes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = iTunes.txt; sourceTree = ""; }; 38 | A861CAEA1D2BFB5F00BA0BAC /* SWMainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWMainWindowController.swift; sourceTree = ""; }; 39 | A865212A1D09698B00C26D0F /* SWNewAccountWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWNewAccountWindowController.swift; sourceTree = ""; }; 40 | A865212B1D09698B00C26D0F /* SWNewAccountWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWNewAccountWindowController.xib; sourceTree = ""; }; 41 | A8C795011CF9317300FA3A7F /* Switcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Switcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | A8C795041CF9317300FA3A7F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | A8C795061CF9317300FA3A7F /* SWMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SWMainViewController.swift; sourceTree = ""; }; 44 | A8C795081CF9317300FA3A7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | A8C7950B1CF9317300FA3A7F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | A8C7950D1CF9317300FA3A7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | A8EC4CE11D2BAAE30062C34D /* SWMainPreferenceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWMainPreferenceController.swift; sourceTree = ""; }; 48 | A8EC4CE21D2BAAE30062C34D /* SWMainPreferenceController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWMainPreferenceController.xib; sourceTree = ""; }; 49 | A8EC4CE51D2BB29A0062C34D /* SWPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWPreferences.swift; sourceTree = ""; }; 50 | A8F7C9441D099322009422D2 /* SWAccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWAccountManager.swift; sourceTree = ""; }; 51 | A8F7C9461D09A072009422D2 /* SWAppLoginManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWAppLoginManager.swift; sourceTree = ""; }; 52 | A8F7C9481D09A5E2009422D2 /* SWEnterPasswordWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWEnterPasswordWindowController.swift; sourceTree = ""; }; 53 | A8F7C9491D09A5E2009422D2 /* SWEnterPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWEnterPasswordWindowController.xib; sourceTree = ""; }; 54 | B7DCEC7B9DCEBE912153EB22 /* Pods_Switcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Switcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | A8C794FE1CF9317300FA3A7F /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 84755F5FF870492CD273BFFF /* Pods_Switcher.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 27B521502B09327DE95504F9 /* Frameworks */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | B7DCEC7B9DCEBE912153EB22 /* Pods_Switcher.framework */, 73 | ); 74 | name = Frameworks; 75 | sourceTree = ""; 76 | }; 77 | 9E51D8B3B79CB54C75DD9EDF /* Pods */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 295D74DF87F0F2F4F1347768 /* Pods-Switcher.debug.xcconfig */, 81 | 04DD167AA3688EA083AFAC09 /* Pods-Switcher.release.xcconfig */, 82 | ); 83 | name = Pods; 84 | sourceTree = ""; 85 | }; 86 | A81312181D03235700C40C4A /* Supporting files */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | A8C795041CF9317300FA3A7F /* AppDelegate.swift */, 90 | A8C795081CF9317300FA3A7F /* Assets.xcassets */, 91 | A8C7950D1CF9317300FA3A7F /* Info.plist */, 92 | A85CD37220690F4300406B5A /* AppStore.txt */, 93 | A85CD3742069EAC500406B5A /* iTunes.txt */, 94 | ); 95 | name = "Supporting files"; 96 | sourceTree = ""; 97 | }; 98 | A8C794F81CF9317300FA3A7F = { 99 | isa = PBXGroup; 100 | children = ( 101 | A8C795031CF9317300FA3A7F /* Switcher */, 102 | A8C795021CF9317300FA3A7F /* Products */, 103 | 9E51D8B3B79CB54C75DD9EDF /* Pods */, 104 | 27B521502B09327DE95504F9 /* Frameworks */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | A8C795021CF9317300FA3A7F /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | A8C795011CF9317300FA3A7F /* Switcher.app */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | A8C795031CF9317300FA3A7F /* Switcher */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | A8EC4CE01D2BAAB90062C34D /* Preference */, 120 | A8C795061CF9317300FA3A7F /* SWMainViewController.swift */, 121 | A81312141D0316F000C40C4A /* SWMainTableCellView.swift */, 122 | A81312161D03223200C40C4A /* SWMainTableView.swift */, 123 | A8C7950A1CF9317300FA3A7F /* Main.storyboard */, 124 | A861CAEA1D2BFB5F00BA0BAC /* SWMainWindowController.swift */, 125 | A865212A1D09698B00C26D0F /* SWNewAccountWindowController.swift */, 126 | A865212B1D09698B00C26D0F /* SWNewAccountWindowController.xib */, 127 | A8F7C9481D09A5E2009422D2 /* SWEnterPasswordWindowController.swift */, 128 | A8F7C9491D09A5E2009422D2 /* SWEnterPasswordWindowController.xib */, 129 | A8F7C9441D099322009422D2 /* SWAccountManager.swift */, 130 | A8F7C9461D09A072009422D2 /* SWAppLoginManager.swift */, 131 | A81312181D03235700C40C4A /* Supporting files */, 132 | ); 133 | path = Switcher; 134 | sourceTree = ""; 135 | }; 136 | A8EC4CE01D2BAAB90062C34D /* Preference */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | A8EC4CE51D2BB29A0062C34D /* SWPreferences.swift */, 140 | A8EC4CE11D2BAAE30062C34D /* SWMainPreferenceController.swift */, 141 | A8EC4CE21D2BAAE30062C34D /* SWMainPreferenceController.xib */, 142 | ); 143 | name = Preference; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | A8C795001CF9317300FA3A7F /* Switcher */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = A8C795101CF9317300FA3A7F /* Build configuration list for PBXNativeTarget "Switcher" */; 152 | buildPhases = ( 153 | 15D77E842251D1E0CC514707 /* [CP] Check Pods Manifest.lock */, 154 | A8C794FD1CF9317300FA3A7F /* Sources */, 155 | A8C794FE1CF9317300FA3A7F /* Frameworks */, 156 | A8C794FF1CF9317300FA3A7F /* Resources */, 157 | FB66DB597074D54676D0AF0A /* [CP] Embed Pods Frameworks */, 158 | 72B7C037C8937FB8D927AEA7 /* [CP] Copy Pods Resources */, 159 | A849E11B206A3C1C00078929 /* ShellScript */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = Switcher; 166 | productName = Switcher; 167 | productReference = A8C795011CF9317300FA3A7F /* Switcher.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | A8C794F91CF9317300FA3A7F /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastSwiftUpdateCheck = 0730; 177 | LastUpgradeCheck = 0730; 178 | ORGANIZATIONNAME = X140Yu; 179 | TargetAttributes = { 180 | A8C795001CF9317300FA3A7F = { 181 | CreatedOnToolsVersion = 7.3.1; 182 | LastSwiftMigration = 0810; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = A8C794FC1CF9317300FA3A7F /* Build configuration list for PBXProject "Switcher" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | Base, 193 | ); 194 | mainGroup = A8C794F81CF9317300FA3A7F; 195 | productRefGroup = A8C795021CF9317300FA3A7F /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | A8C795001CF9317300FA3A7F /* Switcher */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | A8C794FF1CF9317300FA3A7F /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | A85CD3752069EAC500406B5A /* iTunes.txt in Resources */, 210 | A865212D1D09698B00C26D0F /* SWNewAccountWindowController.xib in Resources */, 211 | A8C795091CF9317300FA3A7F /* Assets.xcassets in Resources */, 212 | A85CD37320690F4300406B5A /* AppStore.txt in Resources */, 213 | A8C7950C1CF9317300FA3A7F /* Main.storyboard in Resources */, 214 | A8F7C94B1D09A5E2009422D2 /* SWEnterPasswordWindowController.xib in Resources */, 215 | A8EC4CE41D2BAAE30062C34D /* SWMainPreferenceController.xib in Resources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXResourcesBuildPhase section */ 220 | 221 | /* Begin PBXShellScriptBuildPhase section */ 222 | 15D77E842251D1E0CC514707 /* [CP] Check Pods Manifest.lock */ = { 223 | isa = PBXShellScriptBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputPaths = ( 228 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 229 | "${PODS_ROOT}/Manifest.lock", 230 | ); 231 | name = "[CP] Check Pods Manifest.lock"; 232 | outputPaths = ( 233 | "$(DERIVED_FILE_DIR)/Pods-Switcher-checkManifestLockResult.txt", 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | shellPath = /bin/sh; 237 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 238 | showEnvVarsInLog = 0; 239 | }; 240 | 72B7C037C8937FB8D927AEA7 /* [CP] Copy Pods Resources */ = { 241 | isa = PBXShellScriptBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | inputPaths = ( 246 | ); 247 | name = "[CP] Copy Pods Resources"; 248 | outputPaths = ( 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | shellPath = /bin/sh; 252 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Switcher/Pods-Switcher-resources.sh\"\n"; 253 | showEnvVarsInLog = 0; 254 | }; 255 | A849E11B206A3C1C00078929 /* ShellScript */ = { 256 | isa = PBXShellScriptBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | inputPaths = ( 261 | ); 262 | outputPaths = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\""; 267 | }; 268 | FB66DB597074D54676D0AF0A /* [CP] Embed Pods Frameworks */ = { 269 | isa = PBXShellScriptBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | ); 273 | inputPaths = ( 274 | "${SRCROOT}/Pods/Target Support Files/Pods-Switcher/Pods-Switcher-frameworks.sh", 275 | "${BUILT_PRODUCTS_DIR}/MASPreferences/MASPreferences.framework", 276 | ); 277 | name = "[CP] Embed Pods Frameworks"; 278 | outputPaths = ( 279 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASPreferences.framework", 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | shellPath = /bin/sh; 283 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Switcher/Pods-Switcher-frameworks.sh\"\n"; 284 | showEnvVarsInLog = 0; 285 | }; 286 | /* End PBXShellScriptBuildPhase section */ 287 | 288 | /* Begin PBXSourcesBuildPhase section */ 289 | A8C794FD1CF9317300FA3A7F /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | A8EC4CE61D2BB29A0062C34D /* SWPreferences.swift in Sources */, 294 | A8F7C9471D09A072009422D2 /* SWAppLoginManager.swift in Sources */, 295 | A81312151D0316F000C40C4A /* SWMainTableCellView.swift in Sources */, 296 | A8F7C9451D099322009422D2 /* SWAccountManager.swift in Sources */, 297 | A8C795071CF9317300FA3A7F /* SWMainViewController.swift in Sources */, 298 | A81312171D03223200C40C4A /* SWMainTableView.swift in Sources */, 299 | A8EC4CE31D2BAAE30062C34D /* SWMainPreferenceController.swift in Sources */, 300 | A865212C1D09698B00C26D0F /* SWNewAccountWindowController.swift in Sources */, 301 | A8C795051CF9317300FA3A7F /* AppDelegate.swift in Sources */, 302 | A861CAEB1D2BFB5F00BA0BAC /* SWMainWindowController.swift in Sources */, 303 | A8F7C94A1D09A5E2009422D2 /* SWEnterPasswordWindowController.swift in Sources */, 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | }; 307 | /* End PBXSourcesBuildPhase section */ 308 | 309 | /* Begin PBXVariantGroup section */ 310 | A8C7950A1CF9317300FA3A7F /* Main.storyboard */ = { 311 | isa = PBXVariantGroup; 312 | children = ( 313 | A8C7950B1CF9317300FA3A7F /* Base */, 314 | ); 315 | name = Main.storyboard; 316 | sourceTree = ""; 317 | }; 318 | /* End PBXVariantGroup section */ 319 | 320 | /* Begin XCBuildConfiguration section */ 321 | A8C7950E1CF9317300FA3A7F /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_ANALYZER_NONNULL = YES; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | CODE_SIGN_IDENTITY = "-"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = dwarf; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | ENABLE_TESTABILITY = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_DYNAMIC_NO_PIC = NO; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_OPTIMIZATION_LEVEL = 0; 348 | GCC_PREPROCESSOR_DEFINITIONS = ( 349 | "DEBUG=1", 350 | "$(inherited)", 351 | ); 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | MACOSX_DEPLOYMENT_TARGET = 10.11; 359 | MTL_ENABLE_DEBUG_INFO = YES; 360 | ONLY_ACTIVE_ARCH = YES; 361 | SDKROOT = macosx; 362 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 363 | }; 364 | name = Debug; 365 | }; 366 | A8C7950F1CF9317300FA3A7F /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_ANALYZER_NONNULL = YES; 371 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 372 | CLANG_CXX_LIBRARY = "libc++"; 373 | CLANG_ENABLE_MODULES = YES; 374 | CLANG_ENABLE_OBJC_ARC = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 378 | CLANG_WARN_EMPTY_BODY = YES; 379 | CLANG_WARN_ENUM_CONVERSION = YES; 380 | CLANG_WARN_INT_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_UNREACHABLE_CODE = YES; 383 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 384 | CODE_SIGN_IDENTITY = "-"; 385 | COPY_PHASE_STRIP = NO; 386 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 387 | ENABLE_NS_ASSERTIONS = NO; 388 | ENABLE_STRICT_OBJC_MSGSEND = YES; 389 | GCC_C_LANGUAGE_STANDARD = gnu99; 390 | GCC_NO_COMMON_BLOCKS = YES; 391 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 392 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 393 | GCC_WARN_UNDECLARED_SELECTOR = YES; 394 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 395 | GCC_WARN_UNUSED_FUNCTION = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | MACOSX_DEPLOYMENT_TARGET = 10.11; 398 | MTL_ENABLE_DEBUG_INFO = NO; 399 | SDKROOT = macosx; 400 | }; 401 | name = Release; 402 | }; 403 | A8C795111CF9317300FA3A7F /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | baseConfigurationReference = 295D74DF87F0F2F4F1347768 /* Pods-Switcher.debug.xcconfig */; 406 | buildSettings = { 407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 408 | CLANG_ENABLE_MODULES = YES; 409 | COMBINE_HIDPI_IMAGES = YES; 410 | INFOPLIST_FILE = Switcher/Info.plist; 411 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 412 | MACOSX_DEPLOYMENT_TARGET = 10.10; 413 | PRODUCT_BUNDLE_IDENTIFIER = com.X140Yu.Switcher; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | SWIFT_OBJC_BRIDGING_HEADER = ""; 416 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 417 | SWIFT_VERSION = 4.0; 418 | }; 419 | name = Debug; 420 | }; 421 | A8C795121CF9317300FA3A7F /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | baseConfigurationReference = 04DD167AA3688EA083AFAC09 /* Pods-Switcher.release.xcconfig */; 424 | buildSettings = { 425 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 426 | CLANG_ENABLE_MODULES = YES; 427 | COMBINE_HIDPI_IMAGES = YES; 428 | INFOPLIST_FILE = Switcher/Info.plist; 429 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 430 | MACOSX_DEPLOYMENT_TARGET = 10.10; 431 | PRODUCT_BUNDLE_IDENTIFIER = com.X140Yu.Switcher; 432 | PRODUCT_NAME = "$(TARGET_NAME)"; 433 | SWIFT_OBJC_BRIDGING_HEADER = ""; 434 | SWIFT_VERSION = 4.0; 435 | }; 436 | name = Release; 437 | }; 438 | /* End XCBuildConfiguration section */ 439 | 440 | /* Begin XCConfigurationList section */ 441 | A8C794FC1CF9317300FA3A7F /* Build configuration list for PBXProject "Switcher" */ = { 442 | isa = XCConfigurationList; 443 | buildConfigurations = ( 444 | A8C7950E1CF9317300FA3A7F /* Debug */, 445 | A8C7950F1CF9317300FA3A7F /* Release */, 446 | ); 447 | defaultConfigurationIsVisible = 0; 448 | defaultConfigurationName = Release; 449 | }; 450 | A8C795101CF9317300FA3A7F /* Build configuration list for PBXNativeTarget "Switcher" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | A8C795111CF9317300FA3A7F /* Debug */, 454 | A8C795121CF9317300FA3A7F /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | /* End XCConfigurationList section */ 460 | }; 461 | rootObject = A8C794F91CF9317300FA3A7F /* Project object */; 462 | } 463 | -------------------------------------------------------------------------------- /Switcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Switcher.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Switcher/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 5/28/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import MASPreferences 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | 15 | lazy var preferenceWindowController: NSWindowController = { 16 | let preferenceMain = SWMainPreferenceController() 17 | let _preferenceWindowController = MASPreferencesWindowController(viewControllers: [preferenceMain], title: "Preferences") 18 | return _preferenceWindowController 19 | }() 20 | 21 | func applicationDidFinishLaunching(_ aNotification: Notification) { 22 | // Insert code here to initialize your application 23 | NSApplication.shared.windows[0].isReleasedWhenClosed = false 24 | } 25 | 26 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { 27 | if flag == true { 28 | return false 29 | } else { 30 | NSApplication.shared.windows[0].makeKeyAndOrderFront(self) 31 | return true 32 | } 33 | } 34 | @IBAction func openPreferencesWindow(_ sender: NSMenuItem) { 35 | preferenceWindowController.showWindow(nil) 36 | } 37 | 38 | @IBAction func handleNewAccountItemAction(_ sender: NSMenuItem) { 39 | guard let viewController = NSApplication.shared.mainWindow?.contentViewController as? SWMainViewController else { return } 40 | viewController.showAddAccountWindow(sender) 41 | } 42 | 43 | @IBAction func openGitHubRepoLink(_ sender: NSMenuItem) { 44 | guard let url = URL(string: "https://github.com/X140Yu/Switcher") else { return } 45 | NSWorkspace.shared.open(url) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Switcher/AppStore.txt: -------------------------------------------------------------------------------- 1 | on appIsRunning(appName) 2 | tell application "System Events" to (name of processes) contains appName 3 | end appIsRunning 4 | 5 | on startApp(appName) 6 | set IsRunning to false 7 | set tries to 20 8 | repeat until (IsRunning is true or tries is 0) 9 | set IsRunning to appIsRunning(appName) 10 | set tries to (tries - 1) 11 | delay 0.1 12 | end repeat 13 | end startApp 14 | 15 | startApp("App Store") 16 | 17 | do shell script "open -a App Store" 18 | tell application "System Events" to tell process "App Store" 19 | set frontmost to true 20 | key code 53 21 | try 22 | click menu item 12 of menu 1 of menu bar item 4 of menu bar 1 23 | end try 24 | delay 0.5 25 | click menu item 12 of menu 1 of menu bar item 4 of menu bar 1 26 | repeat until exists sheet 1 of window 1 27 | delay 0.2 28 | end repeat 29 | set value of text field 2 of sheet 1 of window 1 to "$APP_ID" 30 | set value of text field 1 of sheet 1 of window 1 to "$PASSWORD" 31 | delay 0.5 32 | keystroke return 33 | end tell 34 | -------------------------------------------------------------------------------- /Switcher/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 | } -------------------------------------------------------------------------------- /Switcher/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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 434 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 472 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | -------------------------------------------------------------------------------- /Switcher/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 | 0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 X140Yu. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Switcher/SWAccountManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWAccountManager.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/9/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SWAccountManager { 12 | static let sharedInstance = SWAccountManager() 13 | fileprivate init() {} 14 | fileprivate let passwordCache = NSCache() 15 | 16 | fileprivate struct Keys { 17 | static let UserNameArrayKey = "SWUserNameArray" 18 | static let UserNameCountKey = "SWUserNameCount" 19 | } 20 | 21 | // MARK: UserName 22 | static func save(_ userName: String) { 23 | var userNameArray = getUserNameArray() 24 | userNameArray.append(userName) 25 | UserDefaults.standard.set(userNameArray, forKey: Keys.UserNameArrayKey) 26 | } 27 | 28 | static func getUserNameArray() -> [String] { 29 | guard let names = UserDefaults.standard.object(forKey: Keys.UserNameArrayKey) as? [String] else { 30 | return [] 31 | } 32 | return names 33 | } 34 | 35 | static func removeUserNameAt(_ index: Int) { 36 | var userNameArray = getUserNameArray() 37 | userNameArray.remove(at: index) 38 | UserDefaults.standard.set(userNameArray, forKey: Keys.UserNameArrayKey) 39 | } 40 | 41 | static func getUsernameCount() -> Int { 42 | return getUserNameArray().count 43 | } 44 | 45 | // MARK: Password 46 | func getPasswordWith(_ userName: String) -> String? { 47 | if SWPreferences.passwordOnDisk() == false { 48 | return passwordCache.object(forKey: userName as AnyObject) as? String ?? "" 49 | } else { 50 | return UserDefaults.standard.object(forKey: userName) as? String ?? "" 51 | } 52 | } 53 | 54 | func save(_ password: String, with userName: String) { 55 | if SWPreferences.passwordOnDisk() == false { 56 | passwordCache.setObject(password as AnyObject, forKey: userName as AnyObject) 57 | } else { 58 | UserDefaults.standard.set(password, forKey: userName) 59 | } 60 | } 61 | 62 | static func clearPasswordDiskCache() { 63 | let userNameArray = SWAccountManager.getUserNameArray() 64 | userNameArray.forEach { (username) in 65 | UserDefaults.standard.removeObject(forKey: username) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Switcher/SWAppLoginManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWAppLoginManager.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/9/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | class SWAppLoginManager { 13 | 14 | static func loginWith(_ type: SWLoginType, appleID: String, password: String) { 15 | var resourceName: String = "" 16 | switch type { 17 | case .appStore: 18 | resourceName = "AppStore" 19 | case .iTunes: 20 | resourceName = "iTunes" 21 | case .none: 22 | assertionFailure("should not be none") 23 | } 24 | guard let path = Bundle.main.path(forResource: resourceName, ofType: "txt") else { return } 25 | guard let content = try? NSString(contentsOfFile: path, encoding: 0) as String else { return } 26 | guard let appleScript = NSAppleScript(source: processScript(content, appleID: appleID, password: password)) else { return } 27 | 28 | var errorInfo: NSDictionary? = nil 29 | appleScript.executeAndReturnError(&errorInfo) 30 | if errorInfo != nil { 31 | ErrorHanding.showErrorDialog() 32 | } 33 | } 34 | 35 | static func processScript(_ content: String, appleID: String, password: String) -> String { 36 | let result = content.replacingOccurrences(of: "$APP_ID", with: appleID) 37 | return result.replacingOccurrences(of: "$PASSWORD", with: password) 38 | } 39 | } 40 | 41 | struct ErrorHanding { 42 | static func showErrorDialog() { 43 | let alert = NSAlert() 44 | alert.messageText = "Sorry, some errors occured. :(" 45 | alert.informativeText = "Please try again." 46 | alert.addButton(withTitle: "Ok") 47 | alert.runModal() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Switcher/SWEnterPasswordWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWEnterPasswordWindowController.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/9/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum SWLoginType { 12 | case none 13 | case appStore 14 | case iTunes 15 | } 16 | 17 | class SWEnterPasswordWindowController: NSWindowController { 18 | 19 | @IBOutlet weak var passwordTextField: NSSecureTextField! 20 | @IBOutlet weak var prompotTextField: NSTextField! 21 | var userName = "" 22 | var password = "" 23 | var loginType = SWLoginType.none 24 | 25 | override func windowDidLoad() { 26 | super.windowDidLoad() 27 | prompotTextField.stringValue = "Enter password for \(userName)" 28 | passwordTextField.stringValue = password 29 | } 30 | 31 | @IBAction func signIn(_ sender: NSButton) { 32 | let password = passwordTextField.stringValue 33 | guard password.isEmpty == false else { return } 34 | guard let window = window else { return } 35 | SWAccountManager.sharedInstance.save(password, with: userName) 36 | window.sheetParent?.endSheet(window, returnCode: .OK) 37 | SWAppLoginManager.loginWith(loginType, appleID: userName, password: password) 38 | } 39 | 40 | @IBAction func cancel(_ sender: NSButton) { 41 | guard let window = window else { return } 42 | window.sheetParent?.endSheet(window, returnCode: .cancel) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Switcher/SWEnterPasswordWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 50 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | NSAllRomanInputSourcesLocaleIdentifier 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Switcher/SWMainPreferenceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWMainPreferenceController.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 7/5/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import MASPreferences 11 | 12 | class SWMainPreferenceController: NSViewController, MASPreferencesViewController { 13 | 14 | @IBOutlet weak var menuBarBox: NSButton! 15 | @IBOutlet weak var passwordOnDiskBox: NSButton! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | menuBarBox.state = NSControl.StateValue(rawValue: SWPreferences.showMenuBar() == true ? 1 : 0) 21 | passwordOnDiskBox.state = NSControl.StateValue(rawValue: SWPreferences.passwordOnDisk() == true ? 1 : 0) 22 | } 23 | 24 | // MARK: - IBAction 25 | @IBAction func clickMenuBarBox(_ sender: NSButton) { 26 | if sender.state.rawValue == 1 { 27 | SWPreferences.saveShowMenuBarOption(true) 28 | } else { 29 | SWPreferences.saveShowMenuBarOption(false) 30 | } 31 | } 32 | 33 | @IBAction func clickPasswordBox(_ sender: NSButton) { 34 | if sender.state.rawValue == 1 { 35 | SWPreferences.savePasswordOnDiskOption(true) 36 | } else { 37 | SWAccountManager.clearPasswordDiskCache() 38 | SWPreferences.savePasswordOnDiskOption(false) 39 | } 40 | } 41 | 42 | var viewIdentifier: String { 43 | return "GeneralPreference" 44 | } 45 | 46 | var toolbarItemImage: NSImage? { 47 | return NSImage(named: .preferencesGeneral) 48 | } 49 | 50 | var toolbarItemLabel: String? { 51 | return "General" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Switcher/SWMainPreferenceController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Switcher/SWMainTableCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWMainTableCellView.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/4/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SWMainTableCellView: NSTableCellView { 12 | 13 | @IBOutlet weak var emailTextField: NSTextField! 14 | let enterPasswordSheet = SWEnterPasswordWindowController(windowNibName: NSNib.Name(rawValue: "SWEnterPasswordWindowController")) 15 | 16 | @IBAction func loginAppStore(_ sender: NSButton) { 17 | enterPasswordSheet.loginType = SWLoginType.appStore 18 | commonLogin() 19 | } 20 | 21 | @IBAction func loginiTunes(_ sender: NSButton) { 22 | enterPasswordSheet.loginType = SWLoginType.iTunes 23 | commonLogin() 24 | } 25 | 26 | fileprivate func commonLogin() { 27 | let userName = emailTextField.stringValue 28 | if let password = SWAccountManager.sharedInstance.getPasswordWith(userName) { 29 | enterPasswordSheet.password = password 30 | } else { 31 | enterPasswordSheet.password = "" 32 | } 33 | 34 | enterPasswordSheet.userName = userName 35 | 36 | guard let window = enterPasswordSheet.window else { return } 37 | NSApplication.shared.mainWindow?.beginSheet(window, completionHandler: nil) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Switcher/SWMainTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWMainTableView.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/4/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SWMainTableView: NSTableView { 12 | 13 | /** 14 | Draw grid lines in NSTableView only for populated rows. 15 | Origin soultion from http://stackoverflow.com/a/6844340/6343571 16 | */ 17 | override func drawGrid(inClipRect clipRect: NSRect) { 18 | let lastRowRect = rect(ofRow: numberOfRows - 1) 19 | let myClipRect = NSRect(x: 0, y: 0, width: lastRowRect.size.width, height: lastRowRect.maxY) 20 | let finalClipRect = clipRect.intersection(myClipRect) 21 | super.drawGrid(inClipRect: finalClipRect) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Switcher/SWMainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWMainViewController.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 5/28/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol SWAddAccountDelegate: class { 12 | func didAddAccount(_ userName: String) 13 | } 14 | 15 | class SWMainViewController: NSViewController { 16 | 17 | @IBOutlet weak var tableView: NSTableView! 18 | let newAccountSheet = SWNewAccountWindowController(windowNibName: NSNib.Name(rawValue: "SWNewAccountWindowController")) 19 | var userNameArray = SWAccountManager.getUserNameArray() 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | newAccountSheet.delegate = self 24 | } 25 | 26 | func showAddAccountWindow(_ sender: NSMenuItem) { 27 | guard let window = newAccountSheet.window else { return } 28 | sender.isHidden = true 29 | NSApplication.shared.mainWindow?.beginSheet(window) { _ in 30 | sender.isHidden = false 31 | } 32 | } 33 | } 34 | 35 | // MARK: - TableView DataSource 36 | extension SWMainViewController: NSTableViewDataSource, NSTableViewDelegate { 37 | func numberOfRows(in tableView: NSTableView) -> Int { 38 | return userNameArray.count 39 | } 40 | 41 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 42 | guard let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "cell"), owner: self) as? SWMainTableCellView else { return nil } 43 | cell.emailTextField.stringValue = userNameArray[row] 44 | return cell 45 | } 46 | } 47 | 48 | // MARK: - Menu Action 49 | extension SWMainViewController { 50 | @IBAction func deleteAccount(_ sender: NSMenuItem) { 51 | let row = tableView.clickedRow 52 | SWAccountManager.removeUserNameAt(row) 53 | userNameArray = SWAccountManager.getUserNameArray() 54 | tableView.reloadData() 55 | } 56 | } 57 | 58 | // MARK: - Delegate Methods 59 | extension SWMainViewController: SWAddAccountDelegate { 60 | func didAddAccount(_ userName: String) { 61 | SWAccountManager.save(userName) 62 | userNameArray = SWAccountManager.getUserNameArray() 63 | tableView.reloadData() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Switcher/SWMainWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWMainWindowController.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 7/5/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SWMainWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | // http://blog.seedlab.io/currencyx-like-window/ 17 | if let window = self.window { 18 | window.titleVisibility = .hidden 19 | window.titlebarAppearsTransparent = true 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Switcher/SWNewAccountWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWNewAccountWindowController.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 6/9/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SWNewAccountWindowController: NSWindowController { 12 | 13 | weak var delegate: SWAddAccountDelegate? 14 | @IBOutlet weak var textField: NSTextField! 15 | 16 | override func windowDidLoad() { 17 | super.windowDidLoad() 18 | textField.stringValue = "" 19 | } 20 | 21 | @IBAction func addAccount(_ sender: NSButton) { 22 | let appleID = textField.stringValue 23 | guard let window = window else { return } 24 | if appleID.isEmpty == false { 25 | delegate?.didAddAccount(appleID) 26 | window.sheetParent?.endSheet(window, returnCode: .OK) 27 | } 28 | } 29 | 30 | @IBAction func cancel(_ sender: NSButton) { 31 | guard let window = window else { return } 32 | window.sheetParent?.endSheet(window, returnCode: .cancel) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Switcher/SWNewAccountWindowController.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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Switcher/SWPreferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWPreferences.swift 3 | // Switcher 4 | // 5 | // Created by X140Yu on 7/5/16. 6 | // Copyright © 2016 X140Yu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SWPreferences { 12 | 13 | fileprivate struct Keys { 14 | static let ShowMenuBarKey = "SWShowMenuBarKey" 15 | static let PasswordOnDisk = "SWPasswordOnDisk" 16 | } 17 | 18 | static func showMenuBar() -> Bool { 19 | if UserDefaults.standard.object(forKey: Keys.ShowMenuBarKey) != nil { 20 | return UserDefaults.standard.bool(forKey: Keys.ShowMenuBarKey) 21 | } 22 | return true 23 | } 24 | 25 | static func saveShowMenuBarOption(_ option: Bool) { 26 | UserDefaults.standard.set(option, forKey: Keys.ShowMenuBarKey) 27 | } 28 | 29 | // default is false 30 | static func passwordOnDisk() -> Bool { 31 | return UserDefaults.standard.bool(forKey: Keys.PasswordOnDisk) 32 | } 33 | 34 | static func savePasswordOnDiskOption(_ option: Bool) { 35 | UserDefaults.standard.set(option, forKey: Keys.PasswordOnDisk) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Switcher/iTunes.txt: -------------------------------------------------------------------------------- 1 | on appIsRunning(appName) 2 | tell application "System Events" to (name of processes) contains appName 3 | end appIsRunning 4 | 5 | on startApp(appName) 6 | set IsRunning to false 7 | set tries to 20 8 | repeat until (IsRunning is true or tries is 0) 9 | set IsRunning to appIsRunning(appName) 10 | set tries to (tries - 1) 11 | delay 0.1 12 | end repeat 13 | end startApp 14 | 15 | startApp("iTunes") 16 | 17 | do shell script "open -a iTunes" 18 | tell application "System Events" to tell process "iTunes" 19 | set frontmost to true 20 | key code 53 21 | try 22 | click menu item 6 of menu 1 of menu bar item 8 of menu bar 1 23 | end try 24 | delay 0.5 25 | click menu item 2 of menu 1 of menu bar item 8 of menu bar 1 26 | delay 0.2 27 | keystroke "$APP_ID" 28 | keystroke tab 29 | keystroke "$PASSWORD" 30 | keystroke return 31 | end tell 32 | -------------------------------------------------------------------------------- /images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X140Yu/Switcher/bc902cf03c04066e7d35227d195c4c39cd091697/images/01.jpg --------------------------------------------------------------------------------