Class: Pindo::IosConfigParser

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/pindo/config/ios_config_parser.rb

Overview

iOS 配置解析器(单例模式)专门用于解析和处理 iOS 项目的 config.json 配置文件

使用示例:

parser = Pindo::IosConfigParser.instance
parser.load_config(config_file: 'path/to/config.json')

# 获取 Apple ID
apple_id = parser.apple_id

# 获取 Bundle ID 映射
bundle_id_map = parser.get_bundle_id_map

# 命令行参数覆盖
parser.override_apple_id('custom@apple.com')
parser.override_bundle_id('com.example.app')

Constant Summary collapse

ADHOC_BUNDLE_ID_KEYS =

AdHoc 合并时需要替换前缀的所有 Bundle ID 字段

%w[
  app_identifier
  app_identifier_pushcontent
  app_identifier_pushservice
  app_identifier_keyboard
  app_identifier_imessage
  app_identifier_extension
  app_identifier_siri
  app_identifier_siriui
  app_identifier_widget
  app_identifier_extensionad
  app_identifier_extensionporn
  app_identifier_watchapp
  app_identifier_watchapp_extension
].freeze
ADHOC_EXCLUDED_APP_SETTING_KEYS =

AdHoc 合并 app_setting 时需要保留原值(服务器/URL)的 key

%w[
  kGUKeyAppClientHost
  kGUKeyResBaseUrl
  app_web_host
  app_client_url
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeIosConfigParser

Returns a new instance of IosConfigParser.



52
53
54
55
56
57
58
59
60
# File 'lib/pindo/config/ios_config_parser.rb', line 52

def initialize
  @config_file_path = nil
  @config_json = nil
  @config_data = {}
  @overrides = {}
  @app_repo_name = nil  # 保存原始 Bundle ID,用于配置仓库路径
  @adhoc_merged = false # 是否已合并 AdHoc 配置(幂等标志)
  @adhoc_config_dir = nil # 已合并的 AdHoc 配置仓库目录(缓存,避免重复克隆)
end

Instance Attribute Details

#adhoc_config_dirObject (readonly)

Returns the value of attribute adhoc_config_dir.



50
51
52
# File 'lib/pindo/config/ios_config_parser.rb', line 50

def adhoc_config_dir
  @adhoc_config_dir
end

#app_repo_nameObject (readonly)

Returns the value of attribute app_repo_name.



50
51
52
# File 'lib/pindo/config/ios_config_parser.rb', line 50

def app_repo_name
  @app_repo_name
end

#config_file_pathObject (readonly)

Returns the value of attribute config_file_path.



50
51
52
# File 'lib/pindo/config/ios_config_parser.rb', line 50

def config_file_path
  @config_file_path
end

#config_jsonObject (readonly)

Returns the value of attribute config_json.



50
51
52
# File 'lib/pindo/config/ios_config_parser.rb', line 50

def config_json
  @config_json
end

Instance Method Details

#adhoc_merged?Boolean

AdHoc 配置是否已合并(幂等短路用)

Returns:

  • (Boolean)


111
112
113
# File 'lib/pindo/config/ios_config_parser.rb', line 111

def adhoc_merged?
  @adhoc_merged == true
end

#app_id_iosString?

获取 App ID (Apple Store Connect)

Returns:

  • (String, nil)


290
291
292
# File 'lib/pindo/config/ios_config_parser.rb', line 290

def app_id_ios
  @config_data[:app_id_ios]
end

#app_nameString?

获取应用名称

Returns:

  • (String, nil)


278
279
280
# File 'lib/pindo/config/ios_config_parser.rb', line 278

def app_name
  @config_data[:app_name]
end

#app_versionString?

获取应用版本

Returns:

  • (String, nil)


284
285
286
# File 'lib/pindo/config/ios_config_parser.rb', line 284

def app_version
  @config_data[:app_version]
end

#apple_idString?

获取 Apple ID

Returns:

  • (String, nil)

    Apple 账号 ID



119
120
121
# File 'lib/pindo/config/ios_config_parser.rb', line 119

def apple_id
  @overrides[:apple_id] || @config_data[:apple_id]
end

#bundle_idString?

获取主应用 Bundle ID

Returns:

  • (String, nil)

    Bundle ID



145
146
147
# File 'lib/pindo/config/ios_config_parser.rb', line 145

def bundle_id
  @overrides[:bundle_id] || @config_data[:bundle_id]
end

#bundle_id_extensionString?

获取通用 Extension Bundle ID

Returns:

  • (String, nil)


181
182
183
# File 'lib/pindo/config/ios_config_parser.rb', line 181

def bundle_id_extension
  @config_data[:bundle_id_extension]
end

#bundle_id_extensionadString?

获取广告 Extension Bundle ID

Returns:

  • (String, nil)


205
206
207
# File 'lib/pindo/config/ios_config_parser.rb', line 205

def bundle_id_extensionad
  @config_data[:bundle_id_extensionad]
end

#bundle_id_extensionpornString?

获取 Porn Extension Bundle ID

Returns:

  • (String, nil)


211
212
213
# File 'lib/pindo/config/ios_config_parser.rb', line 211

def bundle_id_extensionporn
  @config_data[:bundle_id_extensionporn]
end

#bundle_id_imessageString?

获取 iMessage Extension Bundle ID

Returns:

  • (String, nil)


175
176
177
# File 'lib/pindo/config/ios_config_parser.rb', line 175

def bundle_id_imessage
  @config_data[:bundle_id_imessage]
end

#bundle_id_keyboardString?

获取 Keyboard Extension Bundle ID

Returns:

  • (String, nil)


169
170
171
# File 'lib/pindo/config/ios_config_parser.rb', line 169

def bundle_id_keyboard
  @config_data[:bundle_id_keyboard]
end

#bundle_id_pushcontentString?

获取 Push Content Extension Bundle ID

Returns:

  • (String, nil)


157
158
159
# File 'lib/pindo/config/ios_config_parser.rb', line 157

def bundle_id_pushcontent
  @config_data[:bundle_id_pushcontent]
end

#bundle_id_pushserviceString?

获取 Push Service Extension Bundle ID

Returns:

  • (String, nil)


163
164
165
# File 'lib/pindo/config/ios_config_parser.rb', line 163

def bundle_id_pushservice
  @config_data[:bundle_id_pushservice]
end

#bundle_id_siriString?

获取 Siri Extension Bundle ID

Returns:

  • (String, nil)


187
188
189
# File 'lib/pindo/config/ios_config_parser.rb', line 187

def bundle_id_siri
  @config_data[:bundle_id_siri]
end

#bundle_id_siriuiString?

获取 Siri UI Extension Bundle ID

Returns:

  • (String, nil)


193
194
195
# File 'lib/pindo/config/ios_config_parser.rb', line 193

def bundle_id_siriui
  @config_data[:bundle_id_siriui]
end

#bundle_id_watchappString?

获取 Watch App Bundle ID

Returns:

  • (String, nil)


217
218
219
# File 'lib/pindo/config/ios_config_parser.rb', line 217

def bundle_id_watchapp
  @config_data[:bundle_id_watchapp]
end

#bundle_id_watchapp_extensionString?

获取 Watch App Extension Bundle ID

Returns:

  • (String, nil)


223
224
225
# File 'lib/pindo/config/ios_config_parser.rb', line 223

def bundle_id_watchapp_extension
  @config_data[:bundle_id_watchapp_extension]
end

#bundle_id_widgetString?

获取 Widget Extension Bundle ID

Returns:

  • (String, nil)


199
200
201
# File 'lib/pindo/config/ios_config_parser.rb', line 199

def bundle_id_widget
  @config_data[:bundle_id_widget]
end

#clearObject

清除所有配置和覆盖



99
100
101
102
103
104
105
106
107
# File 'lib/pindo/config/ios_config_parser.rb', line 99

def clear
  @config_file_path = nil
  @config_json = nil
  @config_data = {}
  @overrides = {}
  @app_repo_name = nil
  @adhoc_merged = false
  @adhoc_config_dir = nil
end

#company_nameString?

获取公司名称

Returns:

  • (String, nil)

    公司名称



131
132
133
# File 'lib/pindo/config/ios_config_parser.rb', line 131

def company_name
  @overrides[:company_name] || @config_data[:company_name]
end

#get_bundle_id_arrayArray<String>

获取 Bundle ID 数组(去除 nil 值)

Returns:

  • (Array<String>)

    Bundle ID 数组



256
257
258
# File 'lib/pindo/config/ios_config_parser.rb', line 256

def get_bundle_id_array
  get_bundle_id_map.values
end

#get_bundle_id_mapHash

获取所有 Bundle ID 的映射

Examples:

{
  "bundle_id" => "com.example.app",
  "bundle_id_pushcontent" => "com.example.app.content",
  "bundle_id_pushservice" => "com.example.app.service",
  ...
}

Returns:

  • (Hash)

    Bundle ID 映射,键为类型,值为 Bundle ID



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/pindo/config/ios_config_parser.rb', line 236

def get_bundle_id_map
  {
    "bundle_id" => bundle_id,
    "bundle_id_pushcontent" => bundle_id_pushcontent,
    "bundle_id_pushservice" => bundle_id_pushservice,
    "bundle_id_keyboard" => bundle_id_keyboard,
    "bundle_id_imessage" => bundle_id_imessage,
    "bundle_id_extension" => bundle_id_extension,
    "bundle_id_siri" => bundle_id_siri,
    "bundle_id_siriui" => bundle_id_siriui,
    "bundle_id_widget" => bundle_id_widget,
    "bundle_id_extensionad" => bundle_id_extensionad,
    "bundle_id_extensionporn" => bundle_id_extensionporn,
    "bundle_id_watchapp" => bundle_id_watchapp,
    "bundle_id_watchapp_extension" => bundle_id_watchapp_extension
  }.compact
end

#group_idString?

获取 App Group ID

Returns:

  • (String, nil)


264
265
266
# File 'lib/pindo/config/ios_config_parser.rb', line 264

def group_id
  @config_data[:group_id]
end

#icloud_idString?

获取 iCloud Container ID

Returns:

  • (String, nil)


270
271
272
# File 'lib/pindo/config/ios_config_parser.rb', line 270

def icloud_id
  @config_data[:icloud_id]
end

#ios_deployment_targetString?

获取 iOS 部署目标版本

Returns:

  • (String, nil)


310
311
312
# File 'lib/pindo/config/ios_config_parser.rb', line 310

def ios_deployment_target
  @config_data[:ios_deployment_target]
end

#load_config(config_file:) ⇒ Object

加载配置文件

Parameters:

  • config_file (String)

    config.json 文件路径

Raises:



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
# File 'lib/pindo/config/ios_config_parser.rb', line 65

def load_config(config_file:)
  unless File.exist?(config_file)
    raise "配置文件不存在: #{config_file}"
  end

  @config_file_path = File.expand_path(config_file)

  begin
    @config_json = JSON.parse(File.read(@config_file_path))
  rescue JSON::ParserError => e
    raise "配置文件格式错误: #{e.message}"
  end

  # 从磁盘重新加载的配置是未合并的,重置 AdHoc 标志,
  # 否则后续 merge 会被幂等短路错误跳过,导致用原始配置构建
  @adhoc_merged = false
  @adhoc_config_dir = nil

  # 解析配置
  parse_config_data

  # 检查配置版本
  check_config_version

  self
end

#loaded?Boolean

检查配置是否已加载

Returns:

  • (Boolean)


362
363
364
# File 'lib/pindo/config/ios_config_parser.rb', line 362

def loaded?
  !@config_json.nil?
end

#modify_config_with_adhoc(adhoc_config_dir:) ⇒ Boolean

合并 AdHoc 发布配置

  1. 替换 Apple ID / 公司名为 AdHoc 配置值

  2. 替换所有 Bundle ID(含 Extension、App Group、iCloud)为 AdHoc 前缀(确保使用 AdHoc 证书)

  3. 合并第三方 SDK 配置(Facebook、AdMob、AppLovin)

  4. 合并 app_setting(排除主机和 URL 配置)

幂等:已合并则直接短路,不再二次改写。失败采用 fail-fast:配置文件缺失/解析失败/缺关键字段时抛 Informative,由调用方决定是否中断,避免静默退化为“原始配置”产出错误身份的包。

Parameters:

  • adhoc_config_dir (String)

    AdHoc 配置仓库目录

Returns:

  • (Boolean)

    是否成功合并

Raises:



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/pindo/config/ios_config_parser.rb', line 381

def modify_config_with_adhoc(adhoc_config_dir:)
  raise Informative, "配置未加载,无法合并 AdHoc 配置" unless loaded?
  return true if adhoc_merged?  # 幂等短路
  raise Informative, "AdHoc 配置目录无效" if adhoc_config_dir.nil? || adhoc_config_dir.empty?

  adhoc_config_json = read_adhoc_config(adhoc_config_dir)

  (adhoc_config_json)
  replace_adhoc_bundle_identifiers(adhoc_config_json)
  merge_adhoc_sdk_config(adhoc_config_json)
  merge_adhoc_app_setting(adhoc_config_json)

  # 重新解析配置数据,同步更新 @config_data
  # 这样 bundle_id、bundle_id_pushcontent 等属性方法才能返回替换后的值
  parse_config_data

  @adhoc_merged = true
  @adhoc_config_dir = adhoc_config_dir
  puts "✅ 已替换 Apple ID、Bundle ID 并合并 AdHoc 配置(Facebook、AdMob、AppLovin)"
  true
end

#override_apple_id(value) ⇒ Object

覆盖 Apple ID(命令行参数)

Parameters:

  • value (String)

    Apple ID



125
126
127
# File 'lib/pindo/config/ios_config_parser.rb', line 125

def override_apple_id(value)
  @overrides[:apple_id] = value if value && !value.empty?
end

#override_bundle_id(value) ⇒ Object

覆盖主应用 Bundle ID(命令行参数)

Parameters:

  • value (String)

    Bundle ID



151
152
153
# File 'lib/pindo/config/ios_config_parser.rb', line 151

def override_bundle_id(value)
  @overrides[:bundle_id] = value if value && !value.empty?
end

#override_company_name(value) ⇒ Object

覆盖公司名称

Parameters:

  • value (String)

    公司名称



137
138
139
# File 'lib/pindo/config/ios_config_parser.rb', line 137

def override_company_name(value)
  @overrides[:company_name] = value if value && !value.empty?
end

打印所有配置信息(用于调试)



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/pindo/config/ios_config_parser.rb', line 406

def print_config_info
  puts "\n" + "=" * 80
  puts "iOS 配置信息"
  puts "=" * 80
  puts "配置文件: #{@config_file_path}"
  puts
  puts "【账号信息】"
  puts "  Apple ID: #{apple_id}"
  puts "  Company Name: #{company_name}"
  puts
  puts "【应用信息】"
  puts "  App Name: #{app_name}"
  puts "  App Version: #{app_version}"
  puts "  App ID (iOS): #{app_id_ios}"
  puts
  puts "【Bundle ID】"
  puts "  Main App: #{bundle_id}"
  get_bundle_id_map.each do |type, id|
    next if type == "bundle_id"
    puts "  #{type}: #{id}"
  end
  puts
  puts "【App Group & iCloud】"
  puts "  Group ID: #{group_id}"
  puts "  iCloud ID: #{icloud_id}"
  puts
  puts "【项目信息】"
  puts "  Project Name: #{project_name}"
  puts "  Xcode Build Type: #{xcode_build_type}"
  puts "  iOS Deployment Target: #{ios_deployment_target}"
  puts "=" * 80
  puts
end

#project_nameString?

获取项目名称

Returns:

  • (String, nil)


298
299
300
# File 'lib/pindo/config/ios_config_parser.rb', line 298

def project_name
  @config_data[:project_name]
end

#reloadObject

重新加载配置文件



93
94
95
96
# File 'lib/pindo/config/ios_config_parser.rb', line 93

def reload
  return unless @config_file_path
  load_config(config_file: @config_file_path)
end

#unity_appconfig_valuesHash{String=>String}

提取需要写入 Unity 工程 AppConfig(TextAsset JSON)的字段仅包含在 config.json 中有明确来源的项,已去除 nil/空值

Returns:

  • (Hash{String=>String})

    AppConfig 键 => 值



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/pindo/config/ios_config_parser.rb', line 319

def unity_appconfig_values
  return {} unless loaded?

  app_info = @config_json['app_info'] || {}
  app_setting = @config_json['app_setting'] || {}

  {
    "apple_app_id"        => app_info['app_id_ios'],
    "appsflyer_dev_key"   => app_setting['kGUKeyAppsFlyerDevKey'],
    "support_email"       => app_setting['kGUKeyAppSupportEmail'],
    "fb_app_id"           => app_info['facebook_app_id'],
    "fb_client_token"     => app_info['facebook_client_token'],
    "fb_app_label"        => app_info['app_display_name'],
    "mecha_host"          => app_setting['kGUKeyAppClientHost'],
    "mecha_app_id"        => app_setting['kGUKeyAppPlatform'],
    "google_web_client_id" => app_setting['kGUKeyGoogleWebClientId'],
    "google_ios_client_id" => app_setting['kGUKeyGoogleIosClientId']
  }.each_with_object({}) do |(key, value), result|
    next if value.nil? || value.to_s.strip.empty?
    result[key] = value.to_s.strip
  end
end

#validate_required(*required_keys) ⇒ Object

验证必需的配置项是否存在

Parameters:

  • required_keys (Array<Symbol>)

    必需的配置键

Raises:



347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/pindo/config/ios_config_parser.rb', line 347

def validate_required(*required_keys)
  missing_keys = []

  required_keys.each do |key|
    value = send(key) rescue nil
    missing_keys << key if value.nil? || (value.respond_to?(:empty?) && value.empty?)
  end

  unless missing_keys.empty?
    raise "配置文件缺少必需项: #{missing_keys.join(', ')}"
  end
end

#xcode_build_typeString?

获取 Xcode 构建类型

Returns:

  • (String, nil)


304
305
306
# File 'lib/pindo/config/ios_config_parser.rb', line 304

def xcode_build_type
  @config_data[:xcode_build_type]
end