objc-obfuscator 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +6 -14
  2. data/bin/objc-obfuscator +5 -0
  3. data/lib/objc-obfuscator.rb +6 -0
  4. data/lib/objc-obfuscator/cli.rb +64 -0
  5. data/lib/objc-obfuscator/integrator.rb +59 -0
  6. data/lib/objc-obfuscator/obfuscator.rb +52 -0
  7. data/lib/objc-obfuscator/stringencryptor.rb +33 -0
  8. data/spec/encryptor_spec.rb +0 -0
  9. data/spec/integrator_spec.rb +17 -0
  10. data/spec/string_encryptor_spec.rb +36 -0
  11. data/spec/support/sample_project/Podfile +1 -0
  12. data/spec/support/sample_project/TestProject/TestProject.xcodeproj/project.pbxproj +459 -0
  13. data/spec/support/sample_project/TestProject/TestProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  14. data/spec/support/sample_project/TestProject/TestProject.xcodeproj/project.xcworkspace/xcuserdata/dune.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  15. data/spec/support/sample_project/TestProject/TestProject.xcodeproj/xcuserdata/dune.xcuserdatad/xcschemes/TestProject.xcscheme +96 -0
  16. data/spec/support/sample_project/TestProject/TestProject.xcodeproj/xcuserdata/dune.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  17. data/spec/support/sample_project/TestProject/TestProject/FWTAppDelegate.h +15 -0
  18. data/spec/support/sample_project/TestProject/TestProject/FWTAppDelegate.m +49 -0
  19. data/spec/support/sample_project/TestProject/TestProject/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  20. data/spec/support/sample_project/TestProject/TestProject/Images.xcassets/LaunchImage.launchimage/Contents.json +51 -0
  21. data/spec/support/sample_project/TestProject/TestProject/TestProject-Info.plist +45 -0
  22. data/spec/support/sample_project/TestProject/TestProject/TestProject-Prefix.pch +16 -0
  23. data/spec/support/sample_project/TestProject/TestProject/en.lproj/InfoPlist.strings +2 -0
  24. data/spec/support/sample_project/TestProject/TestProject/main.m +18 -0
  25. data/spec/support/sample_project/TestProject/TestProjectTests/TestProjectTests-Info.plist +22 -0
  26. data/spec/support/sample_project/TestProject/TestProjectTests/TestProjectTests.m +34 -0
  27. data/spec/support/sample_project/TestProject/TestProjectTests/en.lproj/InfoPlist.strings +2 -0
  28. metadata +124 -8
  29. data/lib/obfuscator.rb +0 -46
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NTE2ZmNmOTAzOWQ2NDMzNDljYThiN2I0NWM1NDRiYWEyM2EwYzdiMw==
5
- data.tar.gz: !binary |-
6
- NDg1YjVlZGZmNDg1YTJkOGZjNDk5ZGVlODk2NWUzYmI5MTg1ODU0Mg==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- YWU4YzIzYmQ2ZDRlODRjMzJlYzY1OGVhNTZmYWJmYmI1YjVjYWVhYWUwNmQw
10
- M2E3ZGU5NjM0ZDEwNzAzZjllZDg5ZmY5NDNjZTU3OTQ4ZTg1ZWZkMmQxZmU4
11
- MmI1NTQ0ZWQ5YWI5YWRkZTE4MzQ5NjI1MDJmN2RkYWVjZTRlYjU=
12
- data.tar.gz: !binary |-
13
- ZmY3ZjQ4MjJkZGVlN2MwZGVmMjU1YTU0MzZhNmU4NGVhNzQ1ZmU0NGY2NGQz
14
- ODY4ZmY3NDY2OTUxNWVmNmNkNDhjYzg5MTk0ZTBiYTRmMjIwODljMGY1MmY3
15
- NjYzMDIzYTY2YWNjMDJhYTZiMWYzZmMwZmZmZDYwYzkwZGVkZjQ=
2
+ SHA1:
3
+ metadata.gz: 8ca7ae76fa6cbd837fa588b5fca5385d16cdc533
4
+ data.tar.gz: 005a9d1d2c5a9a2a778a1881c4296fe2fcbb9a63
5
+ SHA512:
6
+ metadata.gz: 72de6e59f6e359d26f293eeb97ad08036b7ad0fc87db410f31034ef30cbf6a9fa327f41500b796636fa194e60847dbd760693b71e825c82d755309c20fd8b0a5
7
+ data.tar.gz: 27561859c13e993aaf455968f0adaae75217b965bf527aeaedb84e3997b248f9607799f62b6f69874a9c27723618a60e590dcf3abe312bce3bc526c8064122c1
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'objc-obfuscator'
4
+
5
+ Objc_Obfuscator::CLI.start(ARGV)
@@ -0,0 +1,6 @@
1
+ require 'objc-obfuscator/obfuscator'
2
+ require 'objc-obfuscator/stringencryptor'
3
+ require 'objc-obfuscator/cli'
4
+
5
+ module Objc_Obfuscator
6
+ end
@@ -0,0 +1,64 @@
1
+ require 'thor'
2
+ require 'objc-obfuscator/obfuscator'
3
+ require 'objc-obfuscator/integrator'
4
+
5
+
6
+ module Objc_Obfuscator
7
+ class CLI < Thor
8
+
9
+ include Objc_Obfuscator::Obfuscator
10
+ include Objc_Obfuscator::Integrator
11
+ include Thor::Actions
12
+
13
+ desc 'obfuscate [ENCRYPTION_KEY] [FILE_NAMES]', "Obfuscate one or more files using a string as the encryption key."
14
+ long_desc <<-LONGDESC
15
+ The app will scan the files for an occurence of an NSString with the keyword
16
+ '__obfuscated' and replace the character of the string with the base64 encoding
17
+ of the the encrypted string using AES256-cbc and the encryption string specified
18
+ as a parameter.
19
+ LONGDESC
20
+ option :backup,
21
+ :type => :boolean,
22
+ :default => true,
23
+ :desc => 'Saves the original files as a .bak file'
24
+ def obfuscate(key, *file_paths)
25
+
26
+ raise Thor::Error, 'A valid key and file should be provided' if key.empty? || file_paths.empty?
27
+
28
+ file_paths.each do |file_path|
29
+ unless File.exist? file_path
30
+ say_status :warning, "File #{file_path} not found!", :yellow
31
+ else
32
+ Dir.mktmpdir { |tmp_dir| obfuscate_file file_path, tmp_dir, key, options[:backup] }
33
+ end
34
+ end
35
+ end
36
+
37
+ desc 'integrate [PROJECT_FILE]', "Integrate Objc Obfuscator with the Xcode project at PROJECT_FILE."
38
+ long_desc <<-LONGDESC
39
+ Obfuscate build phases will be added to the Xcode project and a Podfile augmented with
40
+ the required library to decode such strings. It requires a valid cocoapods project.
41
+ LONGDESC
42
+ option :podfile,
43
+ :type => :string,
44
+ :default => '',
45
+ :desc => 'Path for the Podfile. Default: same as the project file'
46
+ option :target,
47
+ :type => :string,
48
+ :default => '',
49
+ :desc => 'Xcode project containing the "compile source" build phase. Default: the first target on the list.'
50
+ def integrate(project_path)
51
+ raise Thor::Error, 'A valid project file should be specified' unless File.exist?(project_path)
52
+
53
+ podfile_path = File.join File.dirname(project_path), './Podfile'
54
+ unless options[:podfile].empty?
55
+ podfile_path = options[:podfile]
56
+ end
57
+ raise Thor::Error, 'The project must be using cocoapods' unless File.exist?(podfile_path)
58
+
59
+ integrate_xcode project_path, podfile_path, options[:target]
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ require 'thor'
2
+ require 'xcodeproj'
3
+
4
+ module Objc_Obfuscator
5
+ module Integrator
6
+ def integrate_xcode(project_path, podfile_path, target_name)
7
+ project = Xcodeproj::Project.open project_path
8
+
9
+ main_target = project.targets.first
10
+
11
+ unless target_name.empty?
12
+ main_target = project.targets.select { |a| (a.name == target_name) }.first
13
+ end
14
+
15
+ raise Thor::Error, 'Cannot find the specified target' unless main_target
16
+
17
+ phase_obf = project.new('PBXShellScriptBuildPhase')
18
+ phase_obf.name = "Obfuscate strings"
19
+ phase_obf.shell_path = '/bin/bash'
20
+ phase_obf.shell_script = <<-SCRIPT
21
+ if [ -f "$HOME/.rvm/scripts/rvm" ];
22
+ then
23
+ source $HOME/.rvm/scripts/rvm
24
+ rvm rvmrc trust
25
+ rvm rvmrc load
26
+ fi
27
+ find ${SRCROOT} -name "*.h" -exec objc-obfuscator obfuscate {} \\;
28
+ find ${SRCROOT} -name "*.m" -exec objc-obfuscator obfuscate {} \\;
29
+ SCRIPT
30
+
31
+ phase_unobf = project.new('PBXShellScriptBuildPhase')
32
+ phase_unobf.name = "Unobfuscate strings"
33
+ phase_unobf.shell_path = '/bin/bash'
34
+ phase_unobf.shell_script = <<-SCRIPT
35
+ find ${SRCROOT} -name "*.bak" -exec bash -c 'mv -f "$1" "${1%.bak}"' _ {} \\;
36
+ SCRIPT
37
+
38
+ build_source_phase_idx = main_target.build_phases.index main_target.source_build_phase
39
+ obf_phase_idx = build_source_phase_idx
40
+
41
+ main_target.build_phases.insert obf_phase_idx, phase_obf
42
+
43
+ phase_unobf_idx = build_source_phase_idx+2
44
+ if(phase_unobf_idx >= main_target.build_phases.size)
45
+ main_target.build_phases << phase_unobf
46
+ else
47
+ main_target.build_phases.insert phase_unobf_idx, phase_unobf
48
+ end
49
+
50
+ project.save
51
+
52
+ if File.readlines(podfile_path).grep(/objc_obfuscator/).size == 0
53
+ File.open(podfile_path, 'a') {|f| f.write('pod "FWTObfuscator"') }
54
+ end
55
+
56
+ say_status :info, 'The project has been correctly update. Please run "pod install" to install the required pods', :blue
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ require 'thor'
2
+
3
+
4
+ module Objc_Obfuscator
5
+ module Obfuscator
6
+ def obfuscate_file(filepath, tmp_dir, key, backup=true)
7
+ keyword = '__obfuscated'
8
+ objc_string_regex = /@"(.*)"\s*;/
9
+
10
+ encryptor = Objc_Obfuscator::StringEncryptor.new key
11
+
12
+ source_file_path = filepath
13
+ temp_file_path = File.join tmp_dir, "#{File.basename(source_file_path)}.bak"
14
+
15
+ File.delete(temp_file_path) if File.exist?(temp_file_path)
16
+
17
+ source_file = File.open(source_file_path, 'r')
18
+ dest_file = File.open(temp_file_path, 'w')
19
+
20
+ file_changed = false
21
+ say_status :info, "Processing file: #{source_file_path}", :blue
22
+ while !source_file.eof?
23
+ line = source_file.readline
24
+ if line.include? keyword
25
+ unencrypted_string = line.scan(objc_string_regex).last.first
26
+ if unencrypted_string.empty?
27
+ say_status :warning, "Found an occurrence of #{keyword} but there's no obj-string there: \"#{line}\""
28
+ else
29
+ say_status :info, "Found occurrence of __obfuscated string: '#{unencrypted_string}'."
30
+ encrypted_string = encryptor.encrypt unencrypted_string
31
+ line.slice! keyword # remove keyword
32
+ line = line.gsub(unencrypted_string, encrypted_string) # replace the unencrypted with the encrypted
33
+ file_changed = true
34
+ end
35
+ end
36
+ dest_file.puts line
37
+ end
38
+ source_file.close
39
+ dest_file.close
40
+
41
+ if file_changed
42
+ if backup
43
+ FileUtils.mv source_file_path, "#{source_file_path}.bak"
44
+ else
45
+ File.delete source_file_path
46
+ end
47
+ FileUtils.mv temp_file_path, source_file_path
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ require 'encryptor'
2
+ require 'base64'
3
+
4
+ module Objc_Obfuscator
5
+ class StringEncryptor
6
+ attr_reader :last_iv
7
+ attr_reader :last_salt
8
+ attr_accessor :key
9
+
10
+ class EncryptorError < StandardError
11
+
12
+ end
13
+
14
+ def initialize(key)
15
+ @key = key
16
+ end
17
+
18
+ def encrypt(unencrypted_string)
19
+ raise EncryptorError::Error "FATAL: Can't encrypt with an empty key" if key.empty?
20
+ return '' if unencrypted_string.empty?
21
+ salt = Time.now.to_i.to_s
22
+ iv = OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
23
+ Encryptor.default_options.merge!(:key => key , :iv => iv, :salt => salt)
24
+
25
+ encrypted_string = Base64.strict_encode64 unencrypted_string.encrypt
26
+ @last_iv = Base64.strict_encode64 iv
27
+ @last_salt = Base64.strict_encode64 salt
28
+
29
+ "#{encrypted_string}-#{last_iv}-#{last_salt}"
30
+
31
+ end
32
+ end
33
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ require 'objc-obfuscator/integrator'
2
+ require 'fileutils'
3
+
4
+ describe "StringEncryptor" do
5
+ include ObjC_Obfuscator::Integrator
6
+
7
+ # before do
8
+ # @encryptor = Objc_Obfuscator::StringEncryptor.new 'mySecretKey'
9
+ # end
10
+
11
+ it "integrates the encryptor with xcode" do
12
+ Dir.mktmpdir do |dir|
13
+ FileUtils.cp_r 'spec/support/sample_project', dir
14
+ project_path = File.join dir, 'sample_project', 'TestProject', 'TestProject.xcodeproj'
15
+ podfile_path = File.join dir, 'sample_project', 'Podfile'
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ require 'objc-obfuscator/stringencryptor'
2
+ require 'encryptor'
3
+ require 'base64'
4
+
5
+
6
+ describe "StringEncryptor" do
7
+
8
+ before do
9
+ @encryptor = Objc_Obfuscator::StringEncryptor.new 'mySecretKey'
10
+ end
11
+
12
+ it "encrypts a string into a concatenated base64 format (enc-iv-salt)" do
13
+ unencrypted_string = 'MySecretSecretString'
14
+ encrypted_string = @encryptor.encrypt unencrypted_string
15
+ components = encrypted_string.split '-'
16
+ encrypted_payload = Base64.strict_decode64 components[0]
17
+ iv = components[1]
18
+ salt = components[2]
19
+ @encryptor.last_iv.should eq(iv)
20
+ @encryptor.last_salt.should eq(salt)
21
+ iv = Base64.strict_decode64 iv
22
+ salt = Base64.strict_decode64 salt
23
+
24
+ Encryptor.decrypt(:value => encrypted_payload, :key => 'mySecretKey', :iv => iv, :salt => salt).should eq(unencrypted_string)
25
+ end
26
+
27
+ it "fails with empty key" do
28
+ @encryptor.key = ''
29
+ expect { @encryptor.encrypt 'mystring' }.to raise_error
30
+ end
31
+
32
+ it "doesn't fail with empty string" do
33
+ @encryptor.encrypt('').should eq('')
34
+ end
35
+
36
+ end
@@ -0,0 +1 @@
1
+ # sample podfile
@@ -0,0 +1,459 @@
1
+ // !$*UTF8*$!
2
+ {
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 46;
7
+ objects = {
8
+
9
+ /* Begin PBXBuildFile section */
10
+ B71EE897189665D800C6B549 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE896189665D800C6B549 /* Foundation.framework */; };
11
+ B71EE899189665D800C6B549 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE898189665D800C6B549 /* CoreGraphics.framework */; };
12
+ B71EE89B189665D800C6B549 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE89A189665D800C6B549 /* UIKit.framework */; };
13
+ B71EE8A1189665D800C6B549 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B71EE89F189665D800C6B549 /* InfoPlist.strings */; };
14
+ B71EE8A3189665D800C6B549 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B71EE8A2189665D800C6B549 /* main.m */; };
15
+ B71EE8A7189665D800C6B549 /* FWTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B71EE8A6189665D800C6B549 /* FWTAppDelegate.m */; };
16
+ B71EE8A9189665D800C6B549 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B71EE8A8189665D800C6B549 /* Images.xcassets */; };
17
+ B71EE8B0189665D800C6B549 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE8AF189665D800C6B549 /* XCTest.framework */; };
18
+ B71EE8B1189665D800C6B549 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE896189665D800C6B549 /* Foundation.framework */; };
19
+ B71EE8B2189665D800C6B549 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B71EE89A189665D800C6B549 /* UIKit.framework */; };
20
+ B71EE8BA189665D800C6B549 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B71EE8B8189665D800C6B549 /* InfoPlist.strings */; };
21
+ B71EE8BC189665D800C6B549 /* TestProjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B71EE8BB189665D800C6B549 /* TestProjectTests.m */; };
22
+ /* End PBXBuildFile section */
23
+
24
+ /* Begin PBXContainerItemProxy section */
25
+ B71EE8B3189665D800C6B549 /* PBXContainerItemProxy */ = {
26
+ isa = PBXContainerItemProxy;
27
+ containerPortal = B71EE88B189665D800C6B549 /* Project object */;
28
+ proxyType = 1;
29
+ remoteGlobalIDString = B71EE892189665D800C6B549;
30
+ remoteInfo = TestProject;
31
+ };
32
+ /* End PBXContainerItemProxy section */
33
+
34
+ /* Begin PBXFileReference section */
35
+ B71EE893189665D800C6B549 /* TestProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
36
+ B71EE896189665D800C6B549 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
37
+ B71EE898189665D800C6B549 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
38
+ B71EE89A189665D800C6B549 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
39
+ B71EE89E189665D800C6B549 /* TestProject-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestProject-Info.plist"; sourceTree = "<group>"; };
40
+ B71EE8A0189665D800C6B549 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
41
+ B71EE8A2189665D800C6B549 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
42
+ B71EE8A4189665D800C6B549 /* TestProject-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TestProject-Prefix.pch"; sourceTree = "<group>"; };
43
+ B71EE8A5189665D800C6B549 /* FWTAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FWTAppDelegate.h; sourceTree = "<group>"; };
44
+ B71EE8A6189665D800C6B549 /* FWTAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWTAppDelegate.m; sourceTree = "<group>"; };
45
+ B71EE8A8189665D800C6B549 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
46
+ B71EE8AE189665D800C6B549 /* TestProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
47
+ B71EE8AF189665D800C6B549 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
48
+ B71EE8B7189665D800C6B549 /* TestProjectTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestProjectTests-Info.plist"; sourceTree = "<group>"; };
49
+ B71EE8B9189665D800C6B549 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
50
+ B71EE8BB189665D800C6B549 /* TestProjectTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestProjectTests.m; sourceTree = "<group>"; };
51
+ /* End PBXFileReference section */
52
+
53
+ /* Begin PBXFrameworksBuildPhase section */
54
+ B71EE890189665D800C6B549 /* Frameworks */ = {
55
+ isa = PBXFrameworksBuildPhase;
56
+ buildActionMask = 2147483647;
57
+ files = (
58
+ B71EE899189665D800C6B549 /* CoreGraphics.framework in Frameworks */,
59
+ B71EE89B189665D800C6B549 /* UIKit.framework in Frameworks */,
60
+ B71EE897189665D800C6B549 /* Foundation.framework in Frameworks */,
61
+ );
62
+ runOnlyForDeploymentPostprocessing = 0;
63
+ };
64
+ B71EE8AB189665D800C6B549 /* Frameworks */ = {
65
+ isa = PBXFrameworksBuildPhase;
66
+ buildActionMask = 2147483647;
67
+ files = (
68
+ B71EE8B0189665D800C6B549 /* XCTest.framework in Frameworks */,
69
+ B71EE8B2189665D800C6B549 /* UIKit.framework in Frameworks */,
70
+ B71EE8B1189665D800C6B549 /* Foundation.framework in Frameworks */,
71
+ );
72
+ runOnlyForDeploymentPostprocessing = 0;
73
+ };
74
+ /* End PBXFrameworksBuildPhase section */
75
+
76
+ /* Begin PBXGroup section */
77
+ B71EE88A189665D800C6B549 = {
78
+ isa = PBXGroup;
79
+ children = (
80
+ B71EE89C189665D800C6B549 /* TestProject */,
81
+ B71EE8B5189665D800C6B549 /* TestProjectTests */,
82
+ B71EE895189665D800C6B549 /* Frameworks */,
83
+ B71EE894189665D800C6B549 /* Products */,
84
+ );
85
+ sourceTree = "<group>";
86
+ };
87
+ B71EE894189665D800C6B549 /* Products */ = {
88
+ isa = PBXGroup;
89
+ children = (
90
+ B71EE893189665D800C6B549 /* TestProject.app */,
91
+ B71EE8AE189665D800C6B549 /* TestProjectTests.xctest */,
92
+ );
93
+ name = Products;
94
+ sourceTree = "<group>";
95
+ };
96
+ B71EE895189665D800C6B549 /* Frameworks */ = {
97
+ isa = PBXGroup;
98
+ children = (
99
+ B71EE896189665D800C6B549 /* Foundation.framework */,
100
+ B71EE898189665D800C6B549 /* CoreGraphics.framework */,
101
+ B71EE89A189665D800C6B549 /* UIKit.framework */,
102
+ B71EE8AF189665D800C6B549 /* XCTest.framework */,
103
+ );
104
+ name = Frameworks;
105
+ sourceTree = "<group>";
106
+ };
107
+ B71EE89C189665D800C6B549 /* TestProject */ = {
108
+ isa = PBXGroup;
109
+ children = (
110
+ B71EE8A5189665D800C6B549 /* FWTAppDelegate.h */,
111
+ B71EE8A6189665D800C6B549 /* FWTAppDelegate.m */,
112
+ B71EE8A8189665D800C6B549 /* Images.xcassets */,
113
+ B71EE89D189665D800C6B549 /* Supporting Files */,
114
+ );
115
+ path = TestProject;
116
+ sourceTree = "<group>";
117
+ };
118
+ B71EE89D189665D800C6B549 /* Supporting Files */ = {
119
+ isa = PBXGroup;
120
+ children = (
121
+ B71EE89E189665D800C6B549 /* TestProject-Info.plist */,
122
+ B71EE89F189665D800C6B549 /* InfoPlist.strings */,
123
+ B71EE8A2189665D800C6B549 /* main.m */,
124
+ B71EE8A4189665D800C6B549 /* TestProject-Prefix.pch */,
125
+ );
126
+ name = "Supporting Files";
127
+ sourceTree = "<group>";
128
+ };
129
+ B71EE8B5189665D800C6B549 /* TestProjectTests */ = {
130
+ isa = PBXGroup;
131
+ children = (
132
+ B71EE8BB189665D800C6B549 /* TestProjectTests.m */,
133
+ B71EE8B6189665D800C6B549 /* Supporting Files */,
134
+ );
135
+ path = TestProjectTests;
136
+ sourceTree = "<group>";
137
+ };
138
+ B71EE8B6189665D800C6B549 /* Supporting Files */ = {
139
+ isa = PBXGroup;
140
+ children = (
141
+ B71EE8B7189665D800C6B549 /* TestProjectTests-Info.plist */,
142
+ B71EE8B8189665D800C6B549 /* InfoPlist.strings */,
143
+ );
144
+ name = "Supporting Files";
145
+ sourceTree = "<group>";
146
+ };
147
+ /* End PBXGroup section */
148
+
149
+ /* Begin PBXNativeTarget section */
150
+ B71EE892189665D800C6B549 /* TestProject */ = {
151
+ isa = PBXNativeTarget;
152
+ buildConfigurationList = B71EE8BF189665D800C6B549 /* Build configuration list for PBXNativeTarget "TestProject" */;
153
+ buildPhases = (
154
+ B71EE88F189665D800C6B549 /* Sources */,
155
+ B71EE890189665D800C6B549 /* Frameworks */,
156
+ B71EE891189665D800C6B549 /* Resources */,
157
+ );
158
+ buildRules = (
159
+ );
160
+ dependencies = (
161
+ );
162
+ name = TestProject;
163
+ productName = TestProject;
164
+ productReference = B71EE893189665D800C6B549 /* TestProject.app */;
165
+ productType = "com.apple.product-type.application";
166
+ };
167
+ B71EE8AD189665D800C6B549 /* TestProjectTests */ = {
168
+ isa = PBXNativeTarget;
169
+ buildConfigurationList = B71EE8C2189665D800C6B549 /* Build configuration list for PBXNativeTarget "TestProjectTests" */;
170
+ buildPhases = (
171
+ B71EE8AA189665D800C6B549 /* Sources */,
172
+ B71EE8AB189665D800C6B549 /* Frameworks */,
173
+ B71EE8AC189665D800C6B549 /* Resources */,
174
+ );
175
+ buildRules = (
176
+ );
177
+ dependencies = (
178
+ B71EE8B4189665D800C6B549 /* PBXTargetDependency */,
179
+ );
180
+ name = TestProjectTests;
181
+ productName = TestProjectTests;
182
+ productReference = B71EE8AE189665D800C6B549 /* TestProjectTests.xctest */;
183
+ productType = "com.apple.product-type.bundle.unit-test";
184
+ };
185
+ /* End PBXNativeTarget section */
186
+
187
+ /* Begin PBXProject section */
188
+ B71EE88B189665D800C6B549 /* Project object */ = {
189
+ isa = PBXProject;
190
+ attributes = {
191
+ CLASSPREFIX = FWT;
192
+ LastUpgradeCheck = 0500;
193
+ ORGANIZATIONNAME = "Future Workshops";
194
+ TargetAttributes = {
195
+ B71EE8AD189665D800C6B549 = {
196
+ TestTargetID = B71EE892189665D800C6B549;
197
+ };
198
+ };
199
+ };
200
+ buildConfigurationList = B71EE88E189665D800C6B549 /* Build configuration list for PBXProject "TestProject" */;
201
+ compatibilityVersion = "Xcode 3.2";
202
+ developmentRegion = English;
203
+ hasScannedForEncodings = 0;
204
+ knownRegions = (
205
+ en,
206
+ );
207
+ mainGroup = B71EE88A189665D800C6B549;
208
+ productRefGroup = B71EE894189665D800C6B549 /* Products */;
209
+ projectDirPath = "";
210
+ projectRoot = "";
211
+ targets = (
212
+ B71EE892189665D800C6B549 /* TestProject */,
213
+ B71EE8AD189665D800C6B549 /* TestProjectTests */,
214
+ );
215
+ };
216
+ /* End PBXProject section */
217
+
218
+ /* Begin PBXResourcesBuildPhase section */
219
+ B71EE891189665D800C6B549 /* Resources */ = {
220
+ isa = PBXResourcesBuildPhase;
221
+ buildActionMask = 2147483647;
222
+ files = (
223
+ B71EE8A1189665D800C6B549 /* InfoPlist.strings in Resources */,
224
+ B71EE8A9189665D800C6B549 /* Images.xcassets in Resources */,
225
+ );
226
+ runOnlyForDeploymentPostprocessing = 0;
227
+ };
228
+ B71EE8AC189665D800C6B549 /* Resources */ = {
229
+ isa = PBXResourcesBuildPhase;
230
+ buildActionMask = 2147483647;
231
+ files = (
232
+ B71EE8BA189665D800C6B549 /* InfoPlist.strings in Resources */,
233
+ );
234
+ runOnlyForDeploymentPostprocessing = 0;
235
+ };
236
+ /* End PBXResourcesBuildPhase section */
237
+
238
+ /* Begin PBXSourcesBuildPhase section */
239
+ B71EE88F189665D800C6B549 /* Sources */ = {
240
+ isa = PBXSourcesBuildPhase;
241
+ buildActionMask = 2147483647;
242
+ files = (
243
+ B71EE8A7189665D800C6B549 /* FWTAppDelegate.m in Sources */,
244
+ B71EE8A3189665D800C6B549 /* main.m in Sources */,
245
+ );
246
+ runOnlyForDeploymentPostprocessing = 0;
247
+ };
248
+ B71EE8AA189665D800C6B549 /* Sources */ = {
249
+ isa = PBXSourcesBuildPhase;
250
+ buildActionMask = 2147483647;
251
+ files = (
252
+ B71EE8BC189665D800C6B549 /* TestProjectTests.m in Sources */,
253
+ );
254
+ runOnlyForDeploymentPostprocessing = 0;
255
+ };
256
+ /* End PBXSourcesBuildPhase section */
257
+
258
+ /* Begin PBXTargetDependency section */
259
+ B71EE8B4189665D800C6B549 /* PBXTargetDependency */ = {
260
+ isa = PBXTargetDependency;
261
+ target = B71EE892189665D800C6B549 /* TestProject */;
262
+ targetProxy = B71EE8B3189665D800C6B549 /* PBXContainerItemProxy */;
263
+ };
264
+ /* End PBXTargetDependency section */
265
+
266
+ /* Begin PBXVariantGroup section */
267
+ B71EE89F189665D800C6B549 /* InfoPlist.strings */ = {
268
+ isa = PBXVariantGroup;
269
+ children = (
270
+ B71EE8A0189665D800C6B549 /* en */,
271
+ );
272
+ name = InfoPlist.strings;
273
+ sourceTree = "<group>";
274
+ };
275
+ B71EE8B8189665D800C6B549 /* InfoPlist.strings */ = {
276
+ isa = PBXVariantGroup;
277
+ children = (
278
+ B71EE8B9189665D800C6B549 /* en */,
279
+ );
280
+ name = InfoPlist.strings;
281
+ sourceTree = "<group>";
282
+ };
283
+ /* End PBXVariantGroup section */
284
+
285
+ /* Begin XCBuildConfiguration section */
286
+ B71EE8BD189665D800C6B549 /* Debug */ = {
287
+ isa = XCBuildConfiguration;
288
+ buildSettings = {
289
+ ALWAYS_SEARCH_USER_PATHS = NO;
290
+ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
291
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
292
+ CLANG_CXX_LIBRARY = "libc++";
293
+ CLANG_ENABLE_MODULES = YES;
294
+ CLANG_ENABLE_OBJC_ARC = YES;
295
+ CLANG_WARN_BOOL_CONVERSION = YES;
296
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
297
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
298
+ CLANG_WARN_EMPTY_BODY = YES;
299
+ CLANG_WARN_ENUM_CONVERSION = YES;
300
+ CLANG_WARN_INT_CONVERSION = YES;
301
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
302
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
303
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
304
+ COPY_PHASE_STRIP = NO;
305
+ GCC_C_LANGUAGE_STANDARD = gnu99;
306
+ GCC_DYNAMIC_NO_PIC = NO;
307
+ GCC_OPTIMIZATION_LEVEL = 0;
308
+ GCC_PREPROCESSOR_DEFINITIONS = (
309
+ "DEBUG=1",
310
+ "$(inherited)",
311
+ );
312
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
313
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
314
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
315
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
316
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
317
+ GCC_WARN_UNUSED_FUNCTION = YES;
318
+ GCC_WARN_UNUSED_VARIABLE = YES;
319
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
320
+ ONLY_ACTIVE_ARCH = YES;
321
+ SDKROOT = iphoneos;
322
+ TARGETED_DEVICE_FAMILY = "1,2";
323
+ };
324
+ name = Debug;
325
+ };
326
+ B71EE8BE189665D800C6B549 /* Release */ = {
327
+ isa = XCBuildConfiguration;
328
+ buildSettings = {
329
+ ALWAYS_SEARCH_USER_PATHS = NO;
330
+ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
331
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
332
+ CLANG_CXX_LIBRARY = "libc++";
333
+ CLANG_ENABLE_MODULES = YES;
334
+ CLANG_ENABLE_OBJC_ARC = YES;
335
+ CLANG_WARN_BOOL_CONVERSION = YES;
336
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
337
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
338
+ CLANG_WARN_EMPTY_BODY = YES;
339
+ CLANG_WARN_ENUM_CONVERSION = YES;
340
+ CLANG_WARN_INT_CONVERSION = YES;
341
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
342
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
343
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
344
+ COPY_PHASE_STRIP = YES;
345
+ ENABLE_NS_ASSERTIONS = NO;
346
+ GCC_C_LANGUAGE_STANDARD = gnu99;
347
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
348
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
349
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
350
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
351
+ GCC_WARN_UNUSED_FUNCTION = YES;
352
+ GCC_WARN_UNUSED_VARIABLE = YES;
353
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
354
+ SDKROOT = iphoneos;
355
+ TARGETED_DEVICE_FAMILY = "1,2";
356
+ VALIDATE_PRODUCT = YES;
357
+ };
358
+ name = Release;
359
+ };
360
+ B71EE8C0189665D800C6B549 /* Debug */ = {
361
+ isa = XCBuildConfiguration;
362
+ buildSettings = {
363
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
364
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
365
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
366
+ GCC_PREFIX_HEADER = "TestProject/TestProject-Prefix.pch";
367
+ INFOPLIST_FILE = "TestProject/TestProject-Info.plist";
368
+ PRODUCT_NAME = "$(TARGET_NAME)";
369
+ WRAPPER_EXTENSION = app;
370
+ };
371
+ name = Debug;
372
+ };
373
+ B71EE8C1189665D800C6B549 /* Release */ = {
374
+ isa = XCBuildConfiguration;
375
+ buildSettings = {
376
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
377
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
378
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
379
+ GCC_PREFIX_HEADER = "TestProject/TestProject-Prefix.pch";
380
+ INFOPLIST_FILE = "TestProject/TestProject-Info.plist";
381
+ PRODUCT_NAME = "$(TARGET_NAME)";
382
+ WRAPPER_EXTENSION = app;
383
+ };
384
+ name = Release;
385
+ };
386
+ B71EE8C3189665D800C6B549 /* Debug */ = {
387
+ isa = XCBuildConfiguration;
388
+ buildSettings = {
389
+ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
390
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TestProject.app/TestProject";
391
+ FRAMEWORK_SEARCH_PATHS = (
392
+ "$(SDKROOT)/Developer/Library/Frameworks",
393
+ "$(inherited)",
394
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
395
+ );
396
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
397
+ GCC_PREFIX_HEADER = "TestProject/TestProject-Prefix.pch";
398
+ GCC_PREPROCESSOR_DEFINITIONS = (
399
+ "DEBUG=1",
400
+ "$(inherited)",
401
+ );
402
+ INFOPLIST_FILE = "TestProjectTests/TestProjectTests-Info.plist";
403
+ PRODUCT_NAME = "$(TARGET_NAME)";
404
+ TEST_HOST = "$(BUNDLE_LOADER)";
405
+ WRAPPER_EXTENSION = xctest;
406
+ };
407
+ name = Debug;
408
+ };
409
+ B71EE8C4189665D800C6B549 /* Release */ = {
410
+ isa = XCBuildConfiguration;
411
+ buildSettings = {
412
+ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
413
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TestProject.app/TestProject";
414
+ FRAMEWORK_SEARCH_PATHS = (
415
+ "$(SDKROOT)/Developer/Library/Frameworks",
416
+ "$(inherited)",
417
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
418
+ );
419
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
420
+ GCC_PREFIX_HEADER = "TestProject/TestProject-Prefix.pch";
421
+ INFOPLIST_FILE = "TestProjectTests/TestProjectTests-Info.plist";
422
+ PRODUCT_NAME = "$(TARGET_NAME)";
423
+ TEST_HOST = "$(BUNDLE_LOADER)";
424
+ WRAPPER_EXTENSION = xctest;
425
+ };
426
+ name = Release;
427
+ };
428
+ /* End XCBuildConfiguration section */
429
+
430
+ /* Begin XCConfigurationList section */
431
+ B71EE88E189665D800C6B549 /* Build configuration list for PBXProject "TestProject" */ = {
432
+ isa = XCConfigurationList;
433
+ buildConfigurations = (
434
+ B71EE8BD189665D800C6B549 /* Debug */,
435
+ B71EE8BE189665D800C6B549 /* Release */,
436
+ );
437
+ defaultConfigurationIsVisible = 0;
438
+ defaultConfigurationName = Release;
439
+ };
440
+ B71EE8BF189665D800C6B549 /* Build configuration list for PBXNativeTarget "TestProject" */ = {
441
+ isa = XCConfigurationList;
442
+ buildConfigurations = (
443
+ B71EE8C0189665D800C6B549 /* Debug */,
444
+ B71EE8C1189665D800C6B549 /* Release */,
445
+ );
446
+ defaultConfigurationIsVisible = 0;
447
+ };
448
+ B71EE8C2189665D800C6B549 /* Build configuration list for PBXNativeTarget "TestProjectTests" */ = {
449
+ isa = XCConfigurationList;
450
+ buildConfigurations = (
451
+ B71EE8C3189665D800C6B549 /* Debug */,
452
+ B71EE8C4189665D800C6B549 /* Release */,
453
+ );
454
+ defaultConfigurationIsVisible = 0;
455
+ };
456
+ /* End XCConfigurationList section */
457
+ };
458
+ rootObject = B71EE88B189665D800C6B549 /* Project object */;
459
+ }