문제 상황
기존 프로젝트와 다른 프레임워크를 생성하고 해당 프레임워크에 기존 프로젝트에서 사용하던 Resource 파일들을 모두 옮겼습니다. Font, Image Asset 같은 리소스말이죠.
그리고, 평소처럼 빌드를 하는데 빌드를 돌리자마자 에러가 발생했습니다.
프로젝트에서 Resource 프레임워크에 있는 Resource 파일을 인식하지 못해서 생기는 에러인거 같아서, 다른 프레임워크에 있는 Resource를 프로젝트가 인식할 수 있도록 처리를 해주기로 했습니다.
왜 인식을 못하나요?
현재 프로젝트와 Resource 프레임워크는 같은 Bundle을 사용하지 않습니다.
Apple에서는 Bundle 객체를 사용해서 Bundle에 있는 리소스에 액세스하게 되는데, 프로젝트와 프레임워크가 서로 다른 Bundle를 사용하다보니 따로 처리를 해주지 않으면 프로젝트가 프레임워크 Bundle에 있는 리소스에 접근할 수가 없습니다.
어떻게 인식 시킬 수 있나요?
지금 사용하려는 리소스가 프로젝트의 Bundle이 아닌 Resource 프레임워크에 있다는걸 인식시켜주어야 합니다.
Font 문제 해결
위에서 발생한 문제는 현재 Font가 프로젝트에 등록되지 않았기 때문에 발생하는 문제입니다.
따라서, Font를 프로젝트에 등록해봅시다.
일단, 현재 FontSet의 코드는 이렇게 짜여져 있습니다.
AppFontName이라는 enum 타입이 있고, UIFont를 extension해서 작성한 font 메서드가 있습니다. 해당 메서드에서는 매개변수로 AppFontName 타입을 받아서 UIFont로 설정해줍니다.
public enum AppFontName: String {
case regular = "DungGeunMo"
}
public extension UIFont {
static func font(_ style: AppFontName, ofSize size: CGFloat) -> UIFont {
return UIFont(name: style.rawValue, size: size)!
}
}
Swift
복사
하지만, UIFont에서 name 부분에 넣은 String에 대한 폰트가 현재 프로젝트 내 리소스로 존재하지 않기 때문에 에러가 발생하고 맙니다.
Plist에 등록하면 되지 않나요?
사용하려는 리소스가 프로젝트에 포함되어 있다면 plist에 등록하여 사용하면 됩니다.
하지만, 현재는 다른 프레임워크에 있는 폰트를 가져와야 합니다. 그렇기 때문에, 다른 방식이 필요합니다.
먼저, 현재 Bundle에 대한 값을 가져오는 BundleToken이라는 클래스를 하나 만들어 보겠습니다.
그리고, 클래스 내부에 현재 BundleToken이 포함되어 있는 Bundle을 반환하는 bundle이라는 정적 변수도 하나 만들어둘게요. bundle을 사용해서 Resource 프레임워크의 Bundle 값을 가지고 올 겁니다.
final class BundleToken {
static var bundle: Bundle {
return Bundle(for: BundleToken.self)
}
}
Swift
복사
어떤 Bundle에 포함되어 있는지를 가지고 있는 bundle 변수를 만들었으니, 해당 Bundle의 어느 부분에 우리가 원하는 리소스가 포함되어 있는지 찾고 이를 등록하는 코드를 추가해볼게요.
먼저, Bundle에서 해당 리소스가 어디있는지 찾아서 URL 타입을 반환하는 코드를 AppFontName 내부에 작성해줬습니다.
public enum AppFontName: String {
case regular = "DungGeunMo"
var path: String {
switch self {
case .regular: return "DungGeunMo.otf"
}
}
public func register() {
guard let url = BundleToken.bundle.url(forResource: self.path, withExtension: nil) else { return }
}
}
Swift
복사
이제 반환한 URL 타입을 폰트 관리자에 등록하는 메서드를 사용해서 등록할 겁니다.
public func register() {
guard let url = BundleToken.bundle.url(forResource: self.path, withExtension: nil) else { return }
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
}
Swift
복사
첫 번째 매개변수에는 폰트의 URL를 넣고, 두 번째 매개변수에서는 폰트의 등록 범위를 정의합니다.
해당 메서드를 통해서 폰트가 등록될 수 있게 되었습니다.
하지만, 매번 font 메서드가 불릴 때마다 같은 폰트를 등록하고 싶진 않습니다. 따라서, 필요시에만 폰트를 등록하는 메서드를 만드려고 합니다.
fileprivate func registerIfNeeded() {
if !UIFont.fontNames(forFamilyName: self.rawValue).contains(self.rawValue) {
register()
}
}
Swift
복사
만약, 해당 폰트가 UIFont에 없다면 폰트를 등록하고 아니라면 등록을 하지 않는 코드입니다.
해당 메서드를 이전에 만들어 뒀던 font 메서드에 추가해봅시다.
public extension UIFont {
static func font(_ style: AppFontName, ofSize size: CGFloat) -> UIFont {
style.registerIfNeeded()
return UIFont(name: style.rawValue, size: size)!
}
}
Swift
복사
다시 빌드를 해봅시다.
폰트 리소스는 잘 가져오는데 Asset에 포함된 이미지는 아직 못 가져오는 듯 하네요.
Image에서 가져올 수 있도록 코드를 수정해봅시다. ◝( ˙ ꒳ ˙ )◜
Image 문제 해결
현재 load라는 메서드를 사용해서 이미지를 가져오고 있었습니다.
static func load(name: String) -> UIImage {
guard let image = UIImage(named: name, in: nil, compatibleWith: nil) else {
return UIImage()
}
image.accessibilityIdentifier = name
return image
}
Swift
복사
보시다시피, Bundle 부분이 nil로 설정되어 있어서 프로젝트의 Bundle을 사용한 거 같더라구요.
프레임워크의 Bundle로 설정될 수 있도록 수정해보겠습니다. 이전에 Font 문제를 해결하면서 만든 BundleToken을 사용하면 될 것 같아요.
static func load(name: String) -> UIImage {
let bundle = BundleToken.bundle
guard let image = UIImage(named: name, in: bundle, compatibleWith: nil) else {
return UIImage()
}
image.accessibilityIdentifier = name
return image
}
Swift
복사
현재 프레임워크의 Bundle 값을 UIImage init 메서드 내부에 넣어줬습니다.
다시 빌드해볼게요.
Asset이 잘 적용되는 걸 확인할 수 있습니다. ٩( ᐕ)و