├── .gitignore ├── .rubocop.yml ├── .ruby-version ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.txt ├── README.md ├── README_images └── alamofire.png ├── Rakefile ├── bin ├── carthage-play └── playground ├── cocoapods-playgrounds.gemspec ├── lib ├── cocoapods-playgrounds.rb ├── cocoapods-playgrounds │ ├── command │ │ └── playgrounds.rb │ ├── gem_version.rb │ └── generate │ │ ├── playground.rb │ │ ├── workspace.rb │ │ └── workspace │ │ ├── carthage.rb │ │ └── cocoapods.rb └── cocoapods_plugin.rb └── spec ├── command └── playgrounds_spec.rb ├── generate_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg 3 | .idea/ 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop/require_tools 2 | 3 | Metrics/LineLength: 4 | Max: 130 5 | 6 | Style/Documentation: 7 | Exclude: 8 | - 'spec/**/*' 9 | - 'test/**/*' 10 | - 'bin/carthage-play' 11 | - 'bin/playground' 12 | 13 | Naming/FileName: 14 | Exclude: 15 | - 'lib/cocoapods-playgrounds.rb' 16 | 17 | Lint/Void: 18 | Exclude: 19 | - 'spec/**/*' 20 | 21 | Metrics/AbcSize: 22 | Max: 20 23 | 24 | Metrics/MethodLength: 25 | Max: 25 26 | 27 | Metrics/ClassLength: 28 | Max: 150 29 | 30 | Style/SpecialGlobalVars: 31 | Enabled: false 32 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode9.4 3 | language: ruby 4 | cache: bundler 5 | script: 6 | - bundle exec rake specs 7 | - bundle exec rake rubocop 8 | 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | 7 | gem 'cocoapods', '~> 1.6.0' 8 | 9 | group :development do 10 | gem 'bacon', '~> 1.2.0' 11 | gem 'guard' 12 | gem 'guard-bundler' 13 | gem 'guard-rake' 14 | gem 'guard-rubocop' 15 | gem 'mocha', '~> 1.6.0' 16 | gem 'mocha-on-bacon', '~> 0.2.3' 17 | gem 'prettybacon', '~> 0.0.2' 18 | gem 'rubocop', '~> 0.65.0' 19 | gem 'rubocop-require_tools' 20 | gem 'terminal-notifier-guard', '~> 1.6.1' 21 | end 22 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | cocoapods-playgrounds (1.2.2) 5 | cocoapods (>= 1.0.0, < 2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | CFPropertyList (3.0.0) 11 | activesupport (4.2.11) 12 | i18n (~> 0.7) 13 | minitest (~> 5.1) 14 | thread_safe (~> 0.3, >= 0.3.4) 15 | tzinfo (~> 1.1) 16 | ast (2.4.0) 17 | atomos (0.1.3) 18 | bacon (1.2.0) 19 | claide (1.0.2) 20 | cocoapods (1.6.1) 21 | activesupport (>= 4.0.2, < 5) 22 | claide (>= 1.0.2, < 2.0) 23 | cocoapods-core (= 1.6.1) 24 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 25 | cocoapods-downloader (>= 1.2.2, < 2.0) 26 | cocoapods-plugins (>= 1.0.0, < 2.0) 27 | cocoapods-search (>= 1.0.0, < 2.0) 28 | cocoapods-stats (>= 1.0.0, < 2.0) 29 | cocoapods-trunk (>= 1.3.1, < 2.0) 30 | cocoapods-try (>= 1.1.0, < 2.0) 31 | colored2 (~> 3.1) 32 | escape (~> 0.0.4) 33 | fourflusher (>= 2.2.0, < 3.0) 34 | gh_inspector (~> 1.0) 35 | molinillo (~> 0.6.6) 36 | nap (~> 1.0) 37 | ruby-macho (~> 1.4) 38 | xcodeproj (>= 1.8.1, < 2.0) 39 | cocoapods-core (1.6.1) 40 | activesupport (>= 4.0.2, < 6) 41 | fuzzy_match (~> 2.0.4) 42 | nap (~> 1.0) 43 | cocoapods-deintegrate (1.0.3) 44 | cocoapods-downloader (1.2.2) 45 | cocoapods-plugins (1.0.0) 46 | nap 47 | cocoapods-search (1.0.0) 48 | cocoapods-stats (1.1.0) 49 | cocoapods-trunk (1.3.1) 50 | nap (>= 0.8, < 2.0) 51 | netrc (~> 0.11) 52 | cocoapods-try (1.1.0) 53 | coderay (1.1.2) 54 | colored2 (3.1.2) 55 | concurrent-ruby (1.1.4) 56 | escape (0.0.4) 57 | ffi (1.10.0) 58 | formatador (0.2.5) 59 | fourflusher (2.2.0) 60 | fuzzy_match (2.0.4) 61 | gh_inspector (1.1.3) 62 | guard (2.15.0) 63 | formatador (>= 0.2.4) 64 | listen (>= 2.7, < 4.0) 65 | lumberjack (>= 1.0.12, < 2.0) 66 | nenv (~> 0.1) 67 | notiffany (~> 0.0) 68 | pry (>= 0.9.12) 69 | shellany (~> 0.0) 70 | thor (>= 0.18.1) 71 | guard-bundler (2.2.1) 72 | bundler (>= 1.3.0, < 3) 73 | guard (~> 2.2) 74 | guard-compat (~> 1.1) 75 | guard-compat (1.2.1) 76 | guard-rake (1.0.0) 77 | guard 78 | rake 79 | guard-rubocop (1.3.0) 80 | guard (~> 2.0) 81 | rubocop (~> 0.20) 82 | i18n (0.9.5) 83 | concurrent-ruby (~> 1.0) 84 | jaro_winkler (1.5.2) 85 | listen (3.1.5) 86 | rb-fsevent (~> 0.9, >= 0.9.4) 87 | rb-inotify (~> 0.9, >= 0.9.7) 88 | ruby_dep (~> 1.2) 89 | lumberjack (1.0.13) 90 | metaclass (0.0.4) 91 | method_source (0.9.2) 92 | minitest (5.11.3) 93 | mocha (1.6.0) 94 | metaclass (~> 0.0.1) 95 | mocha-on-bacon (0.2.3) 96 | mocha (>= 0.13.0) 97 | molinillo (0.6.6) 98 | nanaimo (0.2.6) 99 | nap (1.1.0) 100 | nenv (0.3.0) 101 | netrc (0.11.0) 102 | notiffany (0.1.1) 103 | nenv (~> 0.1) 104 | shellany (~> 0.0) 105 | parallel (1.14.0) 106 | parser (2.6.0.0) 107 | ast (~> 2.4.0) 108 | powerpack (0.1.2) 109 | prettybacon (0.0.2) 110 | bacon (~> 1.2) 111 | pry (0.12.2) 112 | coderay (~> 1.1.0) 113 | method_source (~> 0.9.0) 114 | psych (3.1.0) 115 | rainbow (3.0.0) 116 | rake (12.3.2) 117 | rb-fsevent (0.10.3) 118 | rb-inotify (0.10.0) 119 | ffi (~> 1.0) 120 | rubocop (0.65.0) 121 | jaro_winkler (~> 1.5.1) 122 | parallel (~> 1.10) 123 | parser (>= 2.5, != 2.5.1.1) 124 | powerpack (~> 0.1) 125 | psych (>= 3.1.0) 126 | rainbow (>= 2.2.2, < 4.0) 127 | ruby-progressbar (~> 1.7) 128 | unicode-display_width (~> 1.4.0) 129 | rubocop-require_tools (0.1.2) 130 | rubocop (>= 0.49.1) 131 | ruby-macho (1.4.0) 132 | ruby-progressbar (1.10.0) 133 | ruby_dep (1.5.0) 134 | shellany (0.0.1) 135 | terminal-notifier-guard (1.6.4) 136 | thor (0.20.3) 137 | thread_safe (0.3.6) 138 | tzinfo (1.2.5) 139 | thread_safe (~> 0.1) 140 | unicode-display_width (1.4.1) 141 | xcodeproj (1.8.1) 142 | CFPropertyList (>= 2.3.3, < 4.0) 143 | atomos (~> 0.1.3) 144 | claide (>= 1.0.2, < 2.0) 145 | colored2 (~> 3.1) 146 | nanaimo (~> 0.2.6) 147 | 148 | PLATFORMS 149 | ruby 150 | 151 | DEPENDENCIES 152 | bacon (~> 1.2.0) 153 | bundler (~> 1.3) 154 | cocoapods (~> 1.6.0) 155 | cocoapods-playgrounds! 156 | guard 157 | guard-bundler 158 | guard-rake 159 | guard-rubocop 160 | mocha (~> 1.6.0) 161 | mocha-on-bacon (~> 0.2.3) 162 | prettybacon (~> 0.0.2) 163 | rake 164 | rubocop (~> 0.65.0) 165 | rubocop-require_tools 166 | terminal-notifier-guard (~> 1.6.1) 167 | 168 | BUNDLED WITH 169 | 1.17.2 170 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | guard :bundler do 4 | require 'guard/bundler' 5 | require 'guard/bundler/verify' 6 | helper = Guard::Bundler::Verify.new 7 | 8 | files = ['Gemfile'] 9 | files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) } 10 | 11 | # Assume files are symlinked from somewhere 12 | files.each { |file| watch(helper.real_path(file)) } 13 | end 14 | 15 | guard :rubocop do 16 | watch(/.+\.rb$/) 17 | watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) } 18 | end 19 | 20 | if `uname`.match?(/Darwin/) 21 | notification :terminal_notifier, 22 | app_name: 'cocoapods-playgrounds', 23 | activate: 'com.googlecode.iTerm2' 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Boris Bügling 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CocoaPods Playgrounds 2 | 3 | [![RubyGems](https://img.shields.io/gem/v/cocoapods-playgrounds.svg?style=flat)](https://rubygems.org/gems/cocoapods-playgrounds) 4 | [![MIT license](https://img.shields.io/github/license/asmallteapot/cocoapods-playgrounds.svg?style=flat)](https://github.com/asmallteapot/cocoapods-playgrounds/blob/master/LICENSE.txt) 5 | [![Build Status](https://img.shields.io/travis/asmallteapot/cocoapods-playgrounds/master.svg?style=flat)](https://travis-ci.org/asmallteapot/cocoapods-playgrounds) 6 | 7 | Generate a Swift Playground for any CocoaPod or Carthage module. 8 | 9 | ![](README_images/alamofire.png) 10 | 11 | ## Installation 12 | 13 | $ gem install cocoapods-playgrounds 14 | 15 | ## Usage 16 | 17 | ### CocoaPods 18 | 19 | To generate a Playground for a specific Pod: 20 | 21 | $ pod playgrounds Alamofire 22 | 23 | To generate a Playground for a local development Pod: 24 | 25 | $ pod playgrounds ../../../Sources/Alamofire/Alamofire.podspec 26 | 27 | To generate a Playground with multiple Pods: 28 | 29 | $ pod playgrounds RxSwift,RxCocoa 30 | 31 | ### Carthage 32 | 33 | To generate a Playground for a Carthage-enabled library: 34 | 35 | $ carthage-play Alamofire/Alamofire 36 | 37 | Note: This currently assumes that libraries are hosted on GitHub. 38 | 39 | ### CLI 40 | 41 | To generate an empty Playground from the commandline: 42 | 43 | $ playground --platform=ios YOLO 44 | $ open YOLO.playground 45 | -------------------------------------------------------------------------------- /README_images/alamofire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmallteapot/cocoapods-playgrounds/35a984cdfd206a57e17c53137ec86bcc0c10d52a/README_images/alamofire.png -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rubocop/rake_task' 5 | 6 | def specs(dir) 7 | FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ') 8 | end 9 | 10 | desc 'Runs all the specs' 11 | task :specs do 12 | sh "bundle exec bacon #{specs('**')}" 13 | end 14 | 15 | desc 'Lints all the files' 16 | RuboCop::RakeTask.new(:rubocop) 17 | 18 | task default: :specs 19 | -------------------------------------------------------------------------------- /bin/carthage-play: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | if $PROGRAM_NAME == __FILE__ 5 | ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __dir__) 6 | require 'rubygems' 7 | require 'bundler/setup' 8 | $LOAD_PATH.unshift File.expand_path('../lib', __dir__) 9 | end 10 | 11 | require 'claide' 12 | require 'cocoapods-playgrounds/gem_version' 13 | require 'cocoapods-playgrounds/generate/workspace/carthage' 14 | 15 | class PlainInformative < StandardError 16 | include CLAide::InformativeError 17 | end 18 | 19 | class Informative < PlainInformative 20 | def message 21 | "[!] #{super}".ansi.red 22 | end 23 | end 24 | 25 | class Command < CLAide::Command 26 | self.command = 'carthage-play' 27 | self.description = 'Generate a Swift Playground for Carthage-enabled libraries' 28 | self.version = CocoapodsPlaygrounds::VERSION 29 | 30 | self.arguments = [CLAide::Argument.new('NAME', true)] 31 | 32 | def initialize(argv) 33 | @name = argv.shift_argument 34 | super 35 | end 36 | 37 | def validate! 38 | help! 'A library name is required.' if @name.nil? 39 | end 40 | 41 | def run 42 | generator = Pod::CarthageGenerator.new(dependencies: [@name]) 43 | generator.generate 44 | end 45 | end 46 | 47 | Command.run(ARGV) 48 | -------------------------------------------------------------------------------- /bin/playground: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | if $PROGRAM_NAME == __FILE__ 5 | ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __dir__) 6 | require 'rubygems' 7 | require 'bundler/setup' 8 | $LOAD_PATH.unshift File.expand_path('../lib', __dir__) 9 | end 10 | 11 | require 'claide' 12 | require 'cocoapods-playgrounds/command/playgrounds' 13 | require 'cocoapods-playgrounds/gem_version' 14 | require 'cocoapods-playgrounds/generate/playground' 15 | 16 | class PlainInformative < StandardError 17 | include CLAide::InformativeError 18 | end 19 | 20 | class Informative < PlainInformative 21 | def message 22 | "[!] #{super}".ansi.red 23 | end 24 | end 25 | 26 | AVAILABLE = Pod::PlaygroundGenerator.platforms.join(', ') 27 | DEFAULT = :osx 28 | 29 | class Command < CLAide::Command 30 | self.command = 'playground' 31 | self.description = 'Generate Xcode Playgrounds' 32 | self.version = CocoapodsPlaygrounds::VERSION 33 | 34 | self.arguments = [CLAide::Argument.new('NAME', true)] 35 | 36 | def self.options 37 | [ 38 | ['--platform', "Platform to generate for (default: #{DEFAULT}, available: #{AVAILABLE})"] 39 | ] 40 | end 41 | 42 | def initialize(argv) 43 | @name = argv.shift_argument 44 | @platform = argv.option('platform', DEFAULT).to_sym 45 | super 46 | end 47 | 48 | def validate! 49 | help! 'Please specify a name for your Playground' if @name.nil? 50 | end 51 | 52 | def run 53 | generator = Pod::PlaygroundGenerator.new(@platform) 54 | generator.generate(@name) 55 | end 56 | end 57 | 58 | Command.run(ARGV) 59 | -------------------------------------------------------------------------------- /cocoapods-playgrounds.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'cocoapods-playgrounds/gem_version.rb' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'cocoapods-playgrounds' 9 | spec.version = CocoapodsPlaygrounds::VERSION 10 | spec.authors = ['Boris Bügling', 'Ellen Teapot'] 11 | spec.email = ['boris@icculus.org', 'hi@asmallteapot.com'] 12 | spec.summary = 'Generates a Swift Playground for any Pod.' 13 | spec.homepage = 'https://github.com/asmallteapot/cocoapods-playgrounds' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_dependency 'cocoapods', '>= 1.0.0', '< 2.0' 22 | 23 | spec.add_development_dependency 'bundler', '~> 1.3' 24 | spec.add_development_dependency 'rake' 25 | end 26 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods-playgrounds/gem_version' 4 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/command/playgrounds.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods-playgrounds/generate/playground' 4 | require 'cocoapods-playgrounds/generate/workspace/cocoapods' 5 | require 'xcodeproj' 6 | 7 | module Pod 8 | class Command 9 | # Command for generating a workspace that contains one or more CocoaPods dependencies 10 | class Playgrounds < Command 11 | DEFAULT_PLATFORM_NAME = :ios 12 | 13 | self.summary = 'Generates a Swift Playground for any Pod.' 14 | 15 | self.description = <<-DESC 16 | Generates a Swift Playground for any Pod. 17 | DESC 18 | 19 | self.arguments = [CLAide::Argument.new('NAMES', true)] 20 | 21 | def self.options 22 | [ 23 | ['--no-install', 'Skip running `pod install`'], 24 | ['--platform', "Platform to generate for (default: #{DEFAULT_PLATFORM_NAME})"], 25 | ['--platform_version', 'Platform version to generate for ' \ 26 | "(default: #{default_version_for_platform(DEFAULT_PLATFORM_NAME)})"] 27 | ] 28 | end 29 | 30 | def self.default_version_for_platform(platform) 31 | Xcodeproj::Constants.const_get("LAST_KNOWN_#{platform.upcase}_SDK") 32 | end 33 | 34 | def initialize(argv) 35 | arg = argv.shift_argument 36 | @names = arg.split(',') if arg 37 | @install = argv.flag?('install', true) 38 | @platform = argv.option('platform', DEFAULT_PLATFORM_NAME).to_sym 39 | @platform_version = argv.option('platform_version', Playgrounds.default_version_for_platform(@platform)) 40 | super 41 | end 42 | 43 | def validate! 44 | super 45 | help! 'At least one Pod name is required.' unless @names 46 | end 47 | 48 | def run 49 | # TODO: Pass platform and deployment target from configuration 50 | generator = CocoaPodsGenerator.new(dependencies: @names, platform: @platform, deployment_target: @platform_version) 51 | generator.generate(install: @install) 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/gem_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module CocoapodsPlaygrounds 4 | VERSION = '1.2.2' 5 | end 6 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/generate/playground.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'fileutils' 4 | require 'pathname' 5 | 6 | module Pod 7 | # Generates a Swift playground 8 | class PlaygroundGenerator 9 | def initialize(platform, import_names = []) 10 | @platform = platform 11 | @import_names = import_names 12 | end 13 | 14 | def generate(name) 15 | playground_path = Pathname.new(name + '.playground') 16 | FileUtils.mkdir(playground_path) 17 | 18 | contents_swift_path = playground_path + 'Contents.swift' 19 | contents_swift_path.write(contents_swift) 20 | 21 | contents_xcplayground_path = playground_path + 'Contents.xcplayground' 22 | contents_xcplayground_path.write(contents_xcplayground) 23 | 24 | playground_path 25 | end 26 | 27 | def base_framework 28 | if @platform == :macos 29 | 'Cocoa' 30 | else 31 | 'UIKit' 32 | end 33 | end 34 | 35 | def contents_swift_imports 36 | all_import_names = [base_framework, 'PlaygroundSupport'] # + @import_names 37 | all_import_names.map { |name| "import #{name}" }.join("\n") 38 | end 39 | 40 | def contents_swift 41 | <<~CONTENTS_SWIFT 42 | //: Playground - noun: a place where people can play 43 | //: Press Cmd-B to build your CocoaPods. 44 | 45 | #{contents_swift_imports} 46 | 47 | // Uncomment this to let your code keep running in the background. 48 | // PlaygroundPage.current.needsIndefiniteExecution = true 49 | 50 | var str = "Hello, playground" 51 | CONTENTS_SWIFT 52 | end 53 | 54 | def contents_xcplayground 55 | <<~CONTENTS_XML 56 | 57 | 58 | 59 | 60 | CONTENTS_XML 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/generate/workspace.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-playgrounds/generate/playground' 5 | require 'xcodeproj' 6 | 7 | module Pod 8 | # Base class for generating a workspace that contains a playground and its dependencies 9 | class WorkspaceGenerator 10 | def initialize(name: nil, dependencies: [], platform: :ios, deployment_target: '9.0') 11 | @base_name = name || 'Empty' 12 | @dependencies = dependencies 13 | @platform = platform 14 | @deployment_target = deployment_target 15 | 16 | @app_target_name = "#{@base_name}Playground" 17 | 18 | @input_dir = Pathname.getwd 19 | @output_dir = @input_dir + Pathname.new(@base_name) 20 | @output_path_workspace = @output_dir + "#{@base_name}.xcworkspace" 21 | @output_path_project = @output_dir + "#{@base_name}.xcodeproj" 22 | @output_path_playground = @output_dir + "#{@base_name}.playground" 23 | end 24 | 25 | def generate(install: true, open_workspace: install) 26 | FileUtils.rm_rf @output_dir 27 | FileUtils.mkdir_p @output_dir 28 | Dir.chdir(@output_dir) do 29 | generate_spec_file 30 | perform_update if perform_update_by_default? 31 | generate_project 32 | perform_install if install 33 | generate_playground 34 | perform_open_workspace if open_workspace 35 | end 36 | end 37 | 38 | private 39 | 40 | def generate_spec_file 41 | raise NotImplementedError, "#{self.class.name}##{method} must be overridden." 42 | end 43 | 44 | def perform_update 45 | raise NotImplementedError, "#{self.class.name}##{method} must be overridden." 46 | end 47 | 48 | def perform_install 49 | raise NotImplementedError, "#{self.class.name}##{method} must be overridden." 50 | end 51 | 52 | def perform_update_by_default? 53 | raise NotImplementedError, "#{self.class.name}##{method} must be overridden." 54 | end 55 | 56 | def perform_open_workspace 57 | raise NotImplementedError, "#{self.class.name}##{method} must be overridden." 58 | end 59 | 60 | def generate_project 61 | project = Xcodeproj::Project.new(@output_path_project) 62 | 63 | target = project.new_target(:application, 64 | @app_target_name, 65 | @platform, 66 | @deployment_target) 67 | target.build_configurations.each do |config| 68 | update_build_settings(config) 69 | end 70 | 71 | # TODO: Should be at the root of the project 72 | project.new_file(@output_path_playground) 73 | project.save 74 | end 75 | 76 | def update_build_settings(config) 77 | config.build_settings['ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME'] = 'LaunchImage' 78 | config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = '' 79 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' 80 | config.build_settings['CODE_SIGNING_REQUIRED'] = 'NO' 81 | config.build_settings['DEFINES_MODULE'] = 'NO' 82 | config.build_settings['EMBEDDED_CONTENT_CONTAINS_SWIFT'] = 'NO' 83 | # TODO: define swift version correctly 84 | config.build_settings['SWIFT_VERSION'] = '4.0' 85 | end 86 | 87 | def generate_playground 88 | generator = Pod::PlaygroundGenerator.new(@platform, @dependencies) 89 | generator.generate(@base_name) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/generate/workspace/carthage.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods-playgrounds/generate/workspace' 4 | 5 | module Pod 6 | # Playground workspace generator for Carthage 7 | class CarthageGenerator < Pod::WorkspaceGenerator 8 | def generate_spec_file 9 | File.open('Cartfile', 'w') { |f| f.write(cartfile_contents) } 10 | end 11 | 12 | def perform_update_by_default? 13 | true 14 | end 15 | 16 | def perform_update 17 | Pod::Executable.execute_command('carthage', ['update', '--platform', @platform.to_s]) 18 | end 19 | 20 | def perform_install 21 | Dir.entries(carthage_platform_dir).each do |entry| 22 | next unless entry.end_with?('.framework') 23 | 24 | FileUtils.mkdir_p(derived_data_dir) 25 | FileUtils.cp_r(carthage_platform_dir + entry, derived_data_dir) 26 | end 27 | end 28 | 29 | def perform_open_workspace 30 | `open #{@output_path_project}` 31 | end 32 | 33 | private 34 | 35 | def input_cartfile_path 36 | @input_dir + @base_name 37 | end 38 | 39 | def input_cartfile_contents 40 | File.file?(input_cartfile_path) ? File.read(input_cartfile_path) : nil 41 | end 42 | 43 | def requirement_for_dependency(name, source: 'github') 44 | <<~SPEC 45 | #{source} "#{name}" 46 | SPEC 47 | end 48 | 49 | def generated_cartfile_contents 50 | @dependencies.map do |name| 51 | requirement_for_dependency name 52 | end.join("\n") 53 | end 54 | 55 | def cartfile_contents 56 | input_cartfile_contents || generated_cartfile_contents 57 | end 58 | 59 | def carthage_platform_dir 60 | platform_dir = Dir.entries('Carthage/Build').find do |dir| 61 | dir.downcase.to_sym == @platform 62 | end 63 | raise "Could not find frameworks for platform #{@platform}" if platform_dir.nil? 64 | 65 | Pathname.new('Carthage/Build') + platform_dir 66 | end 67 | 68 | def derived_data_dir 69 | result = Pod::Executable.execute_command('xcodebuild', 70 | ['-configuration', 'Debug', 71 | '-sdk', 'iphonesimulator', 72 | '-showBuildSettings']) 73 | built_products_dir = result.lines.find do |line| 74 | line[/ BUILT_PRODUCTS_DIR =/] 75 | end.split('=').last.strip 76 | Pathname.new(built_products_dir) 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/cocoapods-playgrounds/generate/workspace/cocoapods.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods-playgrounds/generate/workspace' 4 | 5 | module Pod 6 | # Playground workspace generator for CocoaPods 7 | class CocoaPodsGenerator < Pod::WorkspaceGenerator 8 | def generate_spec_file 9 | File.open('Podfile', 'w') { |f| f.write(podfile_contents) } 10 | end 11 | 12 | def perform_update_by_default? 13 | false 14 | end 15 | 16 | def perform_update 17 | Pod::Executable.execute_command('pod', %w[repo update]) 18 | end 19 | 20 | def perform_install 21 | Pod::Executable.execute_command('pod', ['install', '--no-repo-update']) 22 | end 23 | 24 | def perform_open_workspace 25 | `open #{@output_path_workspace}` 26 | end 27 | 28 | private 29 | 30 | def requirement_for_spec(spec_name, dependency) 31 | local_path = @input_dir + dependency 32 | if local_path.exist? 33 | <<~SPEC 34 | pod '#{spec_name}', path: '#{local_path.dirname}' 35 | SPEC 36 | else 37 | <<~SPEC 38 | pod '#{dependency}' 39 | SPEC 40 | end 41 | end 42 | 43 | def spec_names 44 | @dependencies.map do |name| 45 | abs_path = @input_dir + name 46 | if !abs_path.exist? && name.include?('/') 47 | File.dirname(name) 48 | else 49 | File.basename(name, '.podspec') 50 | end 51 | end 52 | end 53 | 54 | def pods 55 | spec_names.zip(@dependencies).map do |spec_name, dependency| 56 | requirement_for_spec spec_name, dependency 57 | end.join("\n") 58 | end 59 | 60 | def podfile_contents 61 | <<~PODFILE 62 | platform :#{@platform}, '#{@deployment_target}' 63 | use_frameworks! 64 | inhibit_all_warnings! 65 | 66 | target '#{@app_target_name}' do 67 | #{pods} 68 | end 69 | 70 | post_install do |installer| 71 | installer.pods_project.targets.each do |target| 72 | target.build_configurations.each do |config| 73 | config.build_settings['CONFIGURATION_BUILD_DIR'] = '$PODS_CONFIGURATION_BUILD_DIR' 74 | end 75 | end 76 | end 77 | PODFILE 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cocoapods-playgrounds/command/playgrounds' 4 | -------------------------------------------------------------------------------- /spec/command/playgrounds_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('../spec_helper', __dir__) 4 | require 'cocoapods-playgrounds/command/playgrounds' 5 | require 'cocoapods-playgrounds/generate/workspace/cocoapods' 6 | 7 | module Pod 8 | describe Command::Playgrounds do 9 | describe 'CLAide' do 10 | it 'registers it self' do 11 | Command.parse(%w[playgrounds]).should.be.instance_of Command::Playgrounds 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/generate_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('spec_helper', __dir__) 4 | require 'cocoapods-playgrounds/generate/playground' 5 | 6 | module Pod 7 | describe PlaygroundGenerator do 8 | it 'imports the correct base framework for all platforms' do 9 | [:ios, :macos, 'tvos'].map do |platform| 10 | PlaygroundGenerator.new(platform).base_framework 11 | end.should == %w[UIKit Cocoa UIKit] 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | ROOT = Pathname.new(File.expand_path('..', __dir__)) 5 | $LOAD_PATH.unshift((ROOT + 'lib').to_s) 6 | $LOAD_PATH.unshift((ROOT + 'spec').to_s) 7 | 8 | require 'bundler/setup' 9 | require 'bacon' 10 | require 'mocha-on-bacon' 11 | require 'pretty_bacon' 12 | require 'pathname' 13 | require 'cocoapods' 14 | 15 | Mocha::Configuration.prevent(:stubbing_non_existent_method) 16 | 17 | require 'cocoapods_plugin' 18 | 19 | #-----------------------------------------------------------------------------# 20 | 21 | module Pod 22 | # Disable the wrapping so the output is deterministic in the tests. 23 | # 24 | UI.disable_wrap = true 25 | 26 | # Redirects the messages to an internal store. 27 | # 28 | module UI 29 | @output = '' 30 | @warnings = '' 31 | 32 | class << self 33 | attr_accessor :output 34 | attr_accessor :warnings 35 | 36 | def puts(message = '') 37 | @output << "#{message}\n" 38 | end 39 | 40 | def warn(message = '', _actions = []) 41 | @warnings << "#{message}\n" 42 | end 43 | 44 | def print(message) 45 | @output << message 46 | end 47 | end 48 | end 49 | end 50 | 51 | #-----------------------------------------------------------------------------# 52 | --------------------------------------------------------------------------------