Netflix의 URL을 활용한 넷플릭스 영상 추천 앱 CustomNetflix
- Swift, iOS
- 주사용 모듈 및 클래스 : URLSession, AVFoundation, Storyboard 기반
- 외부 라이브러리 : Kingfisher(https://github.com/onevcat/Kingfisher), Firebase(realtime DB)
211108
이 페이지는 아래의 코드와 연결되어있다.
// 상수 db를 FIRDatabaseReference 의 인스턴스화 시켜 위의 페이지에 적힌 DB의 내용을 가져옴
let db = Database.database().reference()
첫 연동 후 프로그래밍 첫 시작의 룰(?ㅋㅋ) 인 "Hello (언어명)!!!"을 가져오고자 시도했으나 print명령에도 console은 묵묵부답.. Firebase에서 DB를 재구성 해보기도 하고, 코드 내부에서 인스턴스를 다시 새로 생성하기도 했으나 여전히 받아오지 못하고 있는 상태였다. 하지만 다시 console에 뜬 메시지들을 처음부터 읽다보니 DB에 접근 자체를 못하고 있길래 firebase 이식할 때 추가했던 .plist를 확인해보니 아래와 같았다.
GoolgeService-Info.plist에 DB URL이 추가 되었는지 확인해보자....해결🤗!
🛠 콘솔 테스트 코드
func updataDBTest() {
db.child("firstData").observeSingleEvent(of: .value) { snapshot in
print("what data in snapshot: \(snapshot)")
// Test Label에 text로 전달하기 위해서 다운캐스팅
let value = snapshot.value as? String ?? ""
DispatchQueue.main.async {
self.dataLabel.text = value
}
print("\(value)")
}
}
콘솔 결과창
211102
- Kingfisher의 최소 타겟은 iOS 12.0이고
- SceneDelegate를 이용해 앱의 UI Lifecycle과 AppDelegate의 Process Lifecycle을 분리 설계하는 디자인 패턴은 iOS 13 부터 사용할 수 있다
UIScene 관련 참조 2019WWDC Scene 설명 영상(https://developer.apple.com/videos/play/wwdc2019/212/)
- 멀티태스킹, 스플릿뷰 와 같이 한 앱이 서로 다른 씬을 갖는 것이 가장 대표적인 가능 사례. (메모앱 동시 스플릿뷰 사용 가능)
211027
- m1과 cocoapods.. cocoapods로 Firebase를 붙이는 과정에서 처음 겪는 문제를 맞닥 뜨렸다.
네이버 로그인 SDK, RealmSwift 같은 외부라이브러리나 SDK설치 하면서 한번도 본 적 없는 에러가 수백줄이 뜨니 당황스러웠다.
// 터미널 Console에 뜬 message
See Crash Report log file under the one of following:
* ~/Library/Logs/DiagnosticReports
* /Library/Logs/DiagnosticReports
for more details.
해결은 역시 구글신. 🤗 😂
cocoapods Github repo의 issue에서 답을 찾을 수 있었다. (CocoaPods/CocoaPods#10446 / CocoaPods/CocoaPods#10446 (comment))
에러코드를 천천히 훑어보니 아키텍쳐에 대한 이야기가 많이 나왔고, 애플실리콘 M1을 탑재한 모델들에서 발생하는 문제로 알려져음
// 아키텍쳐 x86으로 변경해 cocoapods 재설치
sudo arch -arch x86_64 gem install cocoapods
arch -arch x86_64 pod install or other command
- 기존 Intel CPU에서 작동하던 Ruby와 cocoapods의 문제였고, Rosetta2와 아키텍쳐 변경으로 우회적인 방법을 사용해 실행하게 되는 것
- M1 칩 제품이 출시된지 1년이 넘었는데 ARM에서 충돌 없이 기본적으로 잘 실행되는 방법이 있지 않을까? 더 찾아봐야할 문제.
- github.io 블로그 관리를 위해 Jeykll 편집을 할 때도 MacOS 기본으로 깔려있는 Ruby 2.6.8가 M1과 충돌이 생겨 굳이굳이 2.7.2로 새로 설치 뒤 다시 받아서 해결했던 기억이 있다.
- iOS targeting 문제
- 위의 방법으로 cocoapods의 동작문제는 해결했지만 install을 하니 콘솔에 다음과 같은 메시지가 떴다
[!]Automatically assigning platform `iOS` with version `14.5` on target `CustomNetflix` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'CustomNetflix' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for CustomNetflix
pod 'Firebase/Analytics'
end
Podfile에 주석처리가 되어있는 부분 때문에 타겟 버전관련 이슈발생 -> 해제, 버전 변경으로 해결
- 터미널의 동작 방식을 'Rosetta로 열기' 로 설정하고, cocoapods와 ffi 재설치 후 irebase 설치 성공
참조
201019
- UIView에 AVPlayerLayer 적용
- Player View 위에 UIView를 씌워 Player controller 기능 이전(재생,멈춤, 닫기 등)
- play:pause 토글 버튼 기능 구현(동영상 재생, 멈춤)
- close 버튼 클릭 때 AVPlayerItem reset 메소드 적용
- URLSession으로부터 파싱된 정보로 PreviewURL 정보 AVPlayer에 전달
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let movieInfo = movies[indexPath.item]
let movieItemURL = URL(string: movieInfo.previewURL)!
let item = AVPlayerItem(url: movieItemURL)
let PlayerSB = UIStoryboard(name: "Player", bundle: nil)
let PlayerVC = PlayerSB.instantiateViewController(identifier: "PlayerViewController") as! PlayerViewController
PlayerVC.modalPresentationStyle = .fullScreen
// AVPlayer에 동영상 주소 로드
PlayerVC.player.replaceCurrentItem(with: item)
present(PlayerVC, animated: false, completion: nil)
}
- 썸네일 클릭 -> View 전환 -> 자동재생
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 자동재생
play()
}
...
func play() {
player.play()
playBtn.isSelected = ture
}
211013
- Kingfisher 라이브러리를 이용해 URL path에 있는 이미지 캐싱 후 cell의 Image View에 road
- 라이브러리 관련 참조 : https://dev200ok.blogspot.com/2020/09/ios-kingfisher.html
let movieThumb = movies[indexPath.item]
let thumbnailURL = URL(string: movieThumb.thumbnailPath)
SRCresultsCell.movieThumbnail.kf.setImage(with: thumbnailURL)
// image Load Fail
SRCresultsCell.backgroundColor = .systemPink
return SRCresultsCell
- ViewController에 UIView, Play, close Button 배치
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// collectionView selected -> View 전환
let PlayerSB = UIStoryboard(name: "Player", bundle: nil)
let PlayerVC = PlayerSB.instantiateViewController(identifier: "PlayerViewController") as! PlayerViewController
// modal present fullScreen으로 설정 -> iOS13 이후 디폴트는 하단부 팝업형태
PlayerVC.modalPresentationStyle = .fullScreen
present(PlayerVC, animated: false, completion: nil)
}
211011
- searchViewController의 Collection View관련 protocol method 작성
- 검색 결과 중, 검색된 자료 수에 따라 cell을 재사용하여 배치 (3줄), 작동 확인을 위하여 Cell의 배경색을 임의로 systemPink로 설정해 test 진행
UI관련 업데이트이기 때문에 GCD 프로토콜에 따라 main thread로 비동기 처리
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// ....중략
SearchAPI.search(searchTerm) {
movies in
guard let firstMovieName = movies.first?.title else { return print("검색 결과가 없습니다.")}
print("넘어온 영화 개수 : \(movies.count), 첫 번째 영화의 이름은 \(firstMovieName) 입니다. ")
// 검색 완료 후 검색된 데이터를 ViewController에 인스턴스로 호출된 Response structure에 넣고 Cell 업데이트 (비동기처리)
DispatchQueue.main.async {
self.movies = movies
self.resultCollectionView.reloadData()
}
}
print("search Bar clicked: \(searchTerm) 에 대한 검색이 시작되었습니다.")
}
211007 searchAPI class - Searchterm 구현
- search Bar 키보드 표시 관련 dismissKeyboard 함수 구현 - searchTerm - URLSession protocol을 활용한 Data 연동 테스트 -> 검색어 잘 전달되어 내려오는 것을 확인 - 지난 Networking 때 사용한 iTunes URL을 활용해 Test code 작성 (https://github.com/YongJinLeee/networking/blob/main/URLSession_class.playground/Contents.swift)URLSession관련 componenets, query Item 등 URL관련 프로퍼티 구현
let session = URLSession(configuration: .default)
var urlComponents = URLComponents(string: "https://itunes.apple.com/search?")!
let mediaQuery = URLQueryItem(name: "media", value: "music")
let entityQuery = URLQueryItem(name: "entity", value: "song")
let termQuery = URLQueryItem(name: "term", value: term)
urlComponents.queryItems?.append(mediaQuery)
urlComponents.queryItems?.append(entityQuery)
urlComponents.queryItems?.append(termQuery)
let requestURL = urlComponents.url!
let dataTask = session.dataTask(with: requestURL) { data, response, error in
let successRange = 200..<300
// error 테스트 코드
guard error == nil, let statusCode = (response as? HTTPURLResponse)?.statusCode,
successRange.contains(statusCode) else {
print("Error Code: \(successRange)")
completion([])
return
}
guard let resultData = data else {
completion([])
return
}
//data -> [Movie]
let string = String(data: resultData, encoding: .utf8)
print("search URL Operation Test : \(string)")
// completion([Movie])
// Test 후 Movie 구조체에 맞게 데이터 받아오도록 Codable 함수 작성
}
dataTask.resume()
}
}
211006
Netfilx와 유사한 형태의 tap Bar 화면을 구현하고 가장 먼저 URL 테스트를 진행해야 하므로 searchBar 구현