项目模块化后 GoogleMaps SDK 应用程序崩溃

GoogleMaps SDK crashing app after modularization of project

我有一个工作项目,其中包含 GoogleMaps,运行良好。我复制了该项目以使项目模块化,现在我在尝试打开地图时收到 EXC_BAD_ACCESS 错误。

这是我的 pod 配置:

def shared_pods
    ...
    pod 'GoogleMaps', :modular_headers => true
    pod 'GooglePlaces', :modular_headers => true
end

def application_pods
    ...
end


def core_application_pods
...
end


target 'Application' do

  project 'Application/Application.xcodeproj'

  # Pods for Application

  shared_pods
  application_pods

  target 'ApplicationTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'ApplicationUITests' do
    # Pods for testing
  end
end

target 'Core' do

    project 'Core/Core.xcodeproj'

    # Pods for Core

    shared_pods
    core_application_pods

    target 'CoreTests' do
      inherit! :search_paths
      # Pods for testing
    end
end

# Static libraries
static_libraries = ['GoogleMaps', 'GoogleMapsBase', 'GoogleMapsCore']#, 'GooglePlaces']

post_install do |installer|
puts "Running post_install script"

puts "Fix static_libraries"
installer.aggregate_targets.each do |aggregate_target|
    unless ['Pods-Application'].include? aggregate_target.name
        aggregate_target.xcconfigs.each do |config_name, config_file|
            config_file.frameworks.subtract(static_libraries)
            xcconfig_path = aggregate_target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end
...
end

我什至尝试将 pods 都放在 shared_pods、enable/disable modular_headers 中,结果没有区别。

下面是一些创建 UIGMapsView 对象的代码。对象创建成功,地图委托在崩溃前被触发..

final fileprivate func setupMapViewConstraints() {
        if self.mapView == nil {
            let mapView = UIGMapsView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
            mapView.gmapDelegate = self
            mapView.translatesAutoresizingMaskIntoConstraints = false
            mapView.settings.myLocationButton = false
            self.mapView = mapView
        }

        if let mapView = self.mapView {

            self.view.addSubview(mapView)
                self.view.sendSubviewToBack(mapView)

            let views : [String: UIView] = ["mapView": mapView]
            let vertical = NSLayoutConstraint.constraints(withVisualFormat: "V:|[mapView]|", options: [], metrics: nil, views: views)
            let horizontal = NSLayoutConstraint.constraints(withVisualFormat: "H:|[mapView]|", options: [], metrics: nil, views: views)

            NSLayoutConstraint.activate(vertical + horizontal)

            autoreleasepool {
                mapView.startMonitoringLocation()
                mapView.backToMyPosition()
            }
        }
    }

屏幕打开,大约 1 秒后出现崩溃。 (我什至可以看到地图中间的蓝点,但地图全是灰色的...)

我什至尝试让 Zombie 尝试捕捉导致此问题的原因,但也没有太大帮助...这是屏幕截图:

有谁知道可能导致此崩溃的原因吗?

提前致谢!

已编辑:

我尝试了下面提到的解决方案,但在构建时出现了 Link 错误。

Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_GMSServices", referenced from: objc-class-ref in AppDelegate.o "_OBJC_CLASS_$_GMSMapView", referenced from: type metadata for Natura.UIGMapsView in UIGMapsView.o "_OBJC_METACLASS_$_GMSMapView", referenced from: _OBJC_METACLASS_$__TtC6Natura11UIGMapsView in UIGMapsView.o "_OBJC_CLASS_$_GMSMutablePath", referenced from: objc-class-ref in UIGMapsView.o "_OBJC_CLASS_$_GMSCameraPosition", referenced from: objc-class-ref in UIGMapsView.o "_OBJC_CLASS_$_GMSCameraUpdate", referenced from: objc-class-ref in UIGMapsView.o objc-class-ref in SearchConsultantOnMapViewController.o "_OBJC_CLASS_$_GMSMarker", referenced from: objc-class-ref in UIGMapsView.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

我在 post 脚本中添加了一行:

unless ['Pods-Application'].include? aggregate_target.name

所以提出的方案是不设置主工程,只设置Core。但是当我取消上面关于未定义符号的错误时出现

这可能是由于在应用程序中加载多个二进制文件不一致造成的。这通常发生在尝试在多个模块(或多模块架构)中使用静态 pod(如 GoogleMaps)时。

通常我过去为解决此问题所做的工作是将 GoogleMaps 添加到所有模块(这也会导致 GoogleMaps 二进制文件加载到每个单独的模块中),然后在 Podfile 中编写一个 post_install 脚本,通过 Podfile 解析所有生成的 xcconfig 文件并删除所有 GoogleMap 依赖项(-framework GoogleMaps -framework GoogleBundle ...)。这样一来,您的应用程序中只会加载一个 GoogleMaps 的二进制文件,但您的所有模块都可以看到并使用 GoogleMaps.

您不需要手动解析 xcconfig 文件,Cocoapods 确实在 post_install |installer| 中提供了它们,并且 API 提供了强大的修改它们的功能。您可以查看 Cocoapod document 了解更多信息。它将类似于:

# Static libraries
static_libraries = ['GoogleMaps', 'GoogleMapsBase', 'GoogleMapsCore']

target 'Application' do
    pod 'GoogleMaps'
end

target 'Core' do
    project 'Core/Core'
    pod 'GoogleMaps'
end

# Post Installer section
post_install do |installer|
    puts "Running post_install script"

    puts "Fix static_libraries"
    installer.aggregate_targets.each do |aggregate_target|
        aggregate_target.xcconfigs.each do |config_name, config_file|
            config_file.frameworks.subtract(static_libraries)
            xcconfig_path = aggregate_target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end

所以...我终于找到了解决此问题的方法。这并不理想,但考虑到情况,这是最好的选择。

因此,如果您有类似的问题,我希望这对您有所帮助。

GoogleMaps 和 GooglePlaces 如果在 2 个项目中导入则无法正常工作(一些错误会复制 pod,并且您最终会在运行时在项目中使用 2 个框架并导致崩溃,因为它不知道使用哪一个)

我的解决方法是仅在我的核心项目中设置 GoogleMaps 和 GooglePlaces。这是 Podfile:

def shared_pods
    ...
end

def application_pods
    ...
end


def core_application_pods
    ...
    pod 'GoogleMaps', :modular_headers => true
    pod 'GooglePlaces', :modular_headers => true
end


target 'Application' do

  project 'Application/Application.xcodeproj'

  # Pods for Application

  shared_pods
  application_pods

  target 'ApplicationTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'ApplicationUITests' do
    # Pods for testing
  end
end

target 'Core' do

    project 'Core/Core.xcodeproj'

    # Pods for Core

    shared_pods
    core_application_pods

    target 'CoreTests' do
      inherit! :search_paths
      # Pods for testing
    end
end

确保您在构建阶段下的应用程序项目中没有 GoogleMaps 和 GooglePlaces -> Link 带库的二进制文件

我 linked Google我的核心项目中的构建阶段下的位置 -> Link 带有库的二进制文件(你必须手动选择框架,它在 Pods文件夹)。不确定这是否有必要,因为我没有 link Google 地图,它也能正常工作。

创建一个自定义 class,它将用作您的应用程序项目的 shell,以使用 GoogleMaps 框架,例如:

import GoogleMaps

public class NTGMSServices : GMSServices {
    convenience override init() {
        self.init()
    }
}

public class NTGMSCameraUpdate : GMSCameraUpdate {}

import GooglePlaces

public class NTGMSPlace : GMSPlace {}
public class NTGMSAutocompletePrediction : GMSAutocompletePrediction {}
public class NTGMSAutocompleteFetcher : GMSAutocompleteFetcher {
    convenience init() {
        self.init()
    }
}
public class NTGMSPlacesClient : GMSPlacesClient {}
public class NTGMSAutocompleteFilter : GMSAutocompleteFilter {}
public protocol NTGMSAutocompleteFetcherHandlerDelegate  {
    func didAutocomplete(with predictions: [NTGMSAutocompletePrediction])
    func didFailAutocompleteWithError(_ error: Error)
}

public class NTAutocompleteFetcherHandler : NSObject {
    public var delegate: NTGMSAutocompleteFetcherHandlerDelegate?
}
extension NTAutocompleteFetcherHandler : GMSAutocompleteFetcherDelegate {

    public func didAutocomplete(with predictions: [GMSAutocompletePrediction]) {
        delegate?.didAutocomplete(with: predictions as! [NTGMSAutocompletePrediction])
    }

    public func didFailAutocompleteWithError(_ error: Error) {
        delegate?.didFailAutocompleteWithError(error)
    }
}

确保将您在应用程序项目中使用的所有变量更改为这些新类型并导入核心而不是导入 GoogleMaps 或 GooglePlaces 并且如果您正在使用,请调整协议GMSAutocompleteFetcherDelegate

确保清理并重建整个项目。

这样 GoogleMaps 和 GooglePlaces 框架将不会因为 CocoaPods 错误或 Google 框架的实现而重复。

如果 Cocoapods 修复了这个问题,您可以关注 here