티스토리 뷰

앱에 표시하는 컨텐츠를 서버 등에서 받아오는 것이 아니라 앱 내에서 직접 관리해야하는 경우, 일명 하드코딩 해야하는 경우가 있죠. 이럴때 어떻게 하면 좋을까요?

 

가장 쉽게 생각할 수 있는 방법은 static let이 있습니다.

enum TestQuestions {
   static let questions: [TestQuestion] =
    [TestQuestion(title: "첫번째",
                  content: "첫번째 컨텐츠 입니다."),
     TestQuestion(title: "두번째",
                  content: "두번째 컨텐츠 입니다."),
     TestQuestion(title: "세번째",
                  content: "세번째 컨텐츠 입니다."),
     TestQuestion(title: "네번째",
                  content: "네번째 컨텐츠 입니다."),
		// ...
    ]
}

이렇게 enum이나 struct에 static let으로 선언한다면 필요한 곳에서 쉽게 전체 데이터에 접근할 수 있죠.

 

하지만 긴 문자열은 IDE내에서 하드 코딩으로 작성하기 불편한 부분이 있습니다.

이때 plist 파일을 이용하면 더 깔끔하게 정적 컨텐츠를 관리할 수 있습니다.

 

Property List (plist 파일) 추가하기


디렉터리 우클릭 > new file 옵션에서 스크롤하다보면 Resource 섹션이 있습니다. 여기서 먼저 Property List을 추가해보겠습니다.

 

추가를 완료하면 아래와 같이 Key - value 형식의 파일이 생성됩니다.

 

저는 Root를 Array로 만들고 image url, title, content를 가지는 Dictionary를 하위로 생성해주었습니다. 데이터 구조는 각각 상황에 맞게 수정하면 되겠죠?

 

Xcode에서 추가하는게 불편하시다면 다른 IDE로 열어서 xml방식으로 수정할 수도 있습니다!

 

앱 내에서 접근하기


// plist 파일 가져오기 & 파싱
guard let plistPath = Bundle.main.url(forResource: "TestQuestions", withExtension: "plist"),
      let plistData = try? Data(contentsOf: plistPath),
      let plist = try? PropertyListSerialization.propertyList(from: plistData, options: .mutableContainersAndLeaves, format: nil) as? [[String: String]]
else {
    print("plist 파일을 찾을 수 없습니다.")
    return
}

// 결과 출력
plist.forEach {
    print($0["image_url"], $0["title"], $0["content"])
}

앱 내에서 plist 파일의 정보를 가져오기 위해선 Bundle.main.url을 이용해주면 됩니다.

이렇게 가져온 Data형 객체는 PropertyListSerialization를 통해 직렬화, 즉 파싱할 수 있습니다.

저는 Root를 Array로 설정하고 내부에 여러 Dictionary를 담아주었기 때문에 [[String: String]]형으로 형변환을 해주었습니다. 구조에 맞게 파싱하시면 됩니다!

 

구조체에 매핑 하기


딕셔너리 형태 자체는 사용하기가 불편하기 때문에 구조체로 매핑해주겠습니다!

struct TestQuestion {
    let imageURL: URL
    let title: String
    let content: String
    
    static func transform(source: [[String: String]]) -> [TestQuestion] {
        return source.compactMap { item in
            guard let imageURLString = item["image_url"],
                  let imageURL = URL(string: imageURLString),
                  let title = item["title"],
                  let content = item["content"]
            else { return nil }
            
            return TestQuestion(imageURL: imageURL, title: title, content: content)
        }
    }
}

형식에 맞게 구조체를 선언해준 뒤 transform 함수를 구현해주었습니다.

 

guard let plistPath = Bundle.main.url(forResource: "TestQuestions", withExtension: "plist"),
      let plistData = try? Data(contentsOf: plistPath),
      let plist = try? PropertyListSerialization.propertyList(from: plistData, options: .mutableContainersAndLeaves, format: nil) as? [[String: String]]
else {
    print("plist 파일을 찾을 수 없습니다.")
    return
}

// 매핑하기
print(TestQuestion.transform(source: plist))

null값도 미리 처리해서 사용하기 편리하게 만들었습니다.

 

추가) RichText(.rtf) 파일 이용하기


개인정보이용약관 같은 텍스트는 배열로 관리할 필요는 없지만, 텍스트 자체의 길이가 매우 길죠. 이럴 경우에는 RichText 파일을 사용할 수 있습니다.

 

디렉터리 우클릭 > new file 을 통해  Rich Text File을 추가해줍니다.

 

그럼 파일이 텍스트 에디터로 열리게 되는데 여기에 텍스트를 넣어주겠습니다.

그리고 마찬가지로 Bundle.main.url을 통해 파일을 읽어주면됩니다!

    guard let rtfPath = Bundle.main.url(forResource: "TestRichText", withExtension: "rtf"),
          let rtfData = try? Data(contentsOf: rtfPath),
          let rtfText = try? NSAttributedString(data: rtfData, options: [:], documentAttributes: nil)
    else {
        print("TestRichText 파일을 찾을 수 없습니다.")
        return
    }

    print(rtfText.string)

 

더보기

 

MarkDownUI라는 라이브러리를 이용해 마크다운도 적용해보았습니다. 텍스트 에디터로 수정할 수 있어서 간편하네요!

 

 

 


csv 파일이나 txt파일을 변환해서 xml이나 rtf파일로 변환해서 적용하면 더욱 편리할 것 같네요!

읽어주셔서 감사합니다.

 

ref.

공식문서 - PropertyListSerialization

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함