라벨이 swift인 게시물 표시

몽고디비 _id 값을 서버에서 내려주면 아이폰과 안드로이드에서 Date 변환하는 방법

보통 서버에서 서비스를 만들때 몽고디비를 많이 사용합니다. 이때 _id 는 날짜와 시간을 기반으로 생성됩니다. ObjectId("62b47ff00000000000000000") 몽고디비 쉘에서는 getTimestamp() 로 날짜와 시간을 가져올 수 있습니다. 서비스에서 ObjectId 방식으로 데이터를 내리고 모바일(아이폰 및 안드로이드)에서 날짜를 변환해서 사용하는 방법을 소개하고자 합니다. 먼저 아이폰에서 사용할 스위프트용 함수입니다. func ObjectIdToDate(id: String) -> Date {   var resultDate = Date()   let endIdx: String.Index = id.index(id.startIndex, offsetBy: 7)   let hex = id[id.startIndex...endIdx]   if let offset = UInt32(hex, radix: 16) {     resultDate = Date(timeIntervalSince1970: TimeInterval(offset))   }   return resultDate } 안드로이드에서 사용할 자바 메소드입니다. public Date ObjectIdToDate(String id) {   String hex = id.substring(0, 8);   Long x = Long.parseLong(hex, 16) * 1000;   return new Date(x); } 이런식으로 공통 함수를 만들어 사용하면 편합니다.

효율적인 줌 조정 알고리즘

아이폰에서 보통 두개의 손가락으로 줌을 조정할 때가 있다. 이때 핀치 스케일이라는 변수를 함수에서 던져준다. pinch.scale 이 값이 보통 0.0 ~ 5.0 정도 나오는거 같다. 로그를 찍어보니 그렇다. 내가 하고 싶은 건 1.0 미만은 1.0으로 고정하고 4.0 이상은 2.0으로 고정하되 나머지 값은 배율로 정하기로 했다. 보통 일단 이렇게 구성한다. if pinch.scale < 1.0 {     zoomFactor = 1.0 } else if pinch.scale > 4.0 {     zoomFactor = 2.0 } else {     zoomFactor = ((pinch.scale - 1.0) / 3) + 1.0 } 이것도 잘 돌아간다. 좀 더 짧고 잘 돌아가게 할 수 없을까? zoomFactor = max(1.0, min(((pinch.scale - 1.0) / 3) + 1.0, 2.0) 이렇게 하면 한줄로 표시된다. 하지만 코드 추적하기 어렵다. 뒤에 오는 사람을 위해 좀 더 알아보기 쉽게 한줄 더 쓰자 let scale = ((pinch.scale - 1.0) / 3) + 1.0 zoomFactor = max(1.0, min(scale, 2.0) 이렇게 하면 보기 쉽고 깔끔한거 같다. 뭐 정답은 없지만... 눈길을 걸을 땐 발자국이 잘 찍히는지 확인하면서 가자. 뒤에 오는 사람을 위해서... 그 걸음이 쌓이면 길이 될테니까....,

MongoDB ObjectId 에서 날짜 추출하는 방법(Swift & java)

 몽고디비의 ObjectId 에서 날짜를 추출하여 사용하는 방법이다. 아이폰을 위한 스위프트 함수로 작성하였다. func ObjectIdToDate(id: String ) -> Date {     var resultDate = Date ()     let endIdx: String . Index = id. index (id. startIndex , offsetBy: 7)     let hex = id[id. startIndex... endIdx]     if let offset = UInt32 (hex, radix: 16) {         resultDate = Date (timeIntervalSince1970: TimeInterval (offset))     }     return resultDate } 안드로이드를 위한 자바로 작성한 코드이다. public Date ObjectIdToDate (String id) { String hex = id.substring( 0 , 8 ) ; Long x = Long. parseLong (hex , 16 ) * 1000 ; return new Date(x) ; } 문자열 중 앞의 8자리가 유닉스 타임의 숫자이다. 서버에서 몽고디비의 _id 값을 Hex 코드로 내려 준다면 당황하지 말고 변환하여 사용하자. 앞의 4바이트가 시간이므로 이를 변환하면 된다. unix epoch 이란 1970.1.1 부터 지나온 시간을 말한다. 대부분 서버에서 이 시간을 사용한다.

swift 에서 mp4 파일 bundle 에 추가하고 url 로 가져올 때 nil 값이 나올 경우

이미지
 아이폰 앱에서 사용할 데모 영상파일을 추가하였습니다. 이 파일을 가져오는 버튼 이벤트에 다음과 같이 추가하였습니다. 실제로 실행해보니 가져오지 못하는 겁니다. 프로젝트엔 파일이 있는데요. 이 경우 프로젝트 파일로 갑니다. 그런 다음 타켓을 선택합니다. 상단 탭에 Build Phases 가 있어요. 이 부분에 파일이 없으면 추가합니다. 제 경우엔 여기에 파일이 없어서 추가했더니 잘 나옵니다. 아마도 처음 추가할 때 복사가 잘 안되었던거 같아요.

swift pickerView toolbar > uiconstraintbasedlayoutdebugging 오류 추적

일단 화면에 PickerView 가 있다. 그리고 그 위에 ToolBar 를 추가하여 확인 버튼을 달았다. 하지만 실행하면 uiconstraintbasedlayoutdebugging 오류가 생성되었음. let toolbar = UIToolbar () toolbar. barStyle = . default toolbar. isTranslucent = true toolbar. tintColor = . black toolbar. sizeToFit () let flexBarButton = UIBarButtonItem (barButtonSystemItem: . flexibleSpace , target: nil , action: nil ) let doneBarButton = UIBarButtonItem (title: "확인" , style: . plain , target: self , action: #selector ( endEditing )) toolbar. items = [flexBarButton, doneBarButton] toolbar. isUserInteractionEnabled = true          birthYearTextField . inputAccessoryView = toolbar cityTextField . inputAccessoryView = toolbar 일단 소스는 위와 같이 추가하였음. 검색을 해보니 툴바와 피커뷰를 같이 생성하라고 하는 등등.... 모두 안되었다. 아래와 같이 하니까 오류 없이 잘 된다. let toolbar = UIToolbar (frame: CGRect (x: 0 , y: 0 , width: UIScreen . main . bounds . width , height: 37 )) 즉, 툴바를 생성할 때 크기를 지정해주면 된다.

whose view is not in the window hierarchy

swift 5로 코드를 작성 중이었다. self.performSegue(withIdentifier: "ResultToMainSegue", sender: nil) 이 코드가 실행 되면서 나는 오류이다. 그냥 버튼에 이벤트로 적용하면 잘 되는데... 구글 애드몹을 적용하여 광고를 시청하고 끝나는 시점에 넣으니 오류가 난다. whose view is not in the window hierarchy 검색해보니 대충 이게 첫번째 뷰가 아니어서 그런다 첫번째 뷰를 찾아서 거기서 명령을 실행해야 한다고 한다. 그렇게 해보았다. 역시 되지 않는다. 내가 해결한 방법은 이것이다. DispatchQueue.main.async {   self.performSefue(withIdentifier: "ResultToMainSegue", sender: nil) } 이렇게 하니 잘된다. 현재 뷰에서 광고뷰로 넘어가고 다시 현재 뷰로 넘어오면서 뷰의 계층 구조가 명확히 정립되지 않은 상태에서 메인창을 호출하니 오류가 날때도 있고 아닐때도 있었을꺼 같다. 항상 기초에 충실해야겠다.

Swift Cocoa FileManager 사용시 Operation not permitted 오류 대처

이미지
Swift 에서 파일을 가져와서 수정할 일이 있어서 FileManager 를 이용하여 파일을 가져오는데 Operation not permitted 오류가 나는 것이다. 보통 아이폰에서는 샌드박스 디렉토리 구조라 그런 오류가 나는데 코코아는 컴퓨터에서 파일을 접근하는게 아닌가? 대부분의 검색은 이 오류에 대한 내용이 없다. 내가 찾은 방법은 다음과 같다. 이것도 겨우 찾은 방법이다. Xcode - project - target - Capabilities - App Sandbox - Off 예전에 없는 기능이 추가로 생긴거 같다. 그것도 Default로 On 이 되어 있음.

단독망에서 Kitura 사용하는 방법

외부망이 연결되어 지지 않는 네트워크가 많다. 보통 보안이라는 이름으로 그렇게 구성되어 있다. 그렇다고 저장소(일단 Repository)를 연결해주지 않는다. 해당 업데이트 들은 CD 로 구워서 이동해야 한다. 그러면서 백신 검사는 한다고 한다. 리눅스 바이러스까지 잡는 V3 대단해요..ㅋㅋ 이런 상황에서 Kitura 를 운영하고 싶다면 먼저 걸리는게 있을 것이다. swift package update 를 하면 외부 저장소가 연결되지 않아 관련 모듈 설치가 안된다. 난 이런 경우 이렇게 해결한다. 일단 외부 연결이 되는 곳에서 해당 파일을 추가하고 업데이트를 한다. $ vi Package.swift .package(url: "https://gitbub.com/IBM-Swift/Kitura", from: "2.7.0") 추가한 다음에 하단에 타켓에 해당 내용을 추가해줘야 한다. 보통 이것을 빼먹어서 오류가 난다. .target(name: "app_name", dependencies: ["Kitura"]) 이제 패키지를 업데이트 한다. $ swift package update 뭐라 업데이트를 많이 한다. 관련 모듈까지 하기 때문이다. 이때 나오는 문구를 잘 적어 두어야 한다. 특히 각 모듈의 버전들..... 이것 때문에 설정이 아주 복잡해질 수 있다. 업데이트가 완료되면 $ cd .build $ cd repositories $ ls 방금 업데이트 한 관련 모듈이 이곳에 다 있다. 이 폴더를 통째로 복사하여 단독망으로 이동하면 된다. 나의 경우는 해당 모듈의 뒷부분은 전부 자르고 이동했다. $ mv Kitura--70115644 Kitura 단독망에서는 이렇게 사용했다. $ mkdir app_name $ cd app_name $ swift package init --type executable 폴더가 만들어지면 이곳에 re

swift Kitura 사용 중 CORS 설정 삽질기

서버용 Swift 웹 프레임 워크인 Kitura를 사용중이다. 이중 CORS 를 설정해야 하는 상황인데 일단 Kitura-CORS 라는 미들웨어가 있다. 이를 문서에 있는 대로 설정했으나.... 역시나 안된다. 다른 애들은 어떻게 되었다는 거지? Package.swift 에 해당 패키지를 추가하였다. .package(url: "https://github.com/IBM-Swift/Kitura-CORS.git", form: "2.1.1") 아래 target 에도 추가해야 한다. .target(name: "app_name", dependencies: ["KituraCORS"]) 이제 main.swift 에 임포트 부터 한다 import KituraCORS let options = Options(allowedOrigin: .all, methods: ["GET", "POST"], allowedHeaders: ["Content-Type"], maxAge: 5) let kors = CORS(options: options) let router = Router() router.all("/cors", middleware: cors) 컴파일을 하고 앱을 실행한 다음 웹서버를 테스트하면 오류가 난다. Access-Control-Allow-Origin 어쩌고 저쩌고...블라블라 설정이 잘못 되었는지 각종 설정을 다 바꾸어 보아도 되지 않는다. 이놈들은 되지도 않는 모듈을 왜 올려 놓은거야... 아님 최신 파일과 맞지 않는건가? 그리하여 CORS 에 대한 문서를 다시 처음부터 읽기 시작하였다. CORS란 Cross-Origin Resource Sharing 으로 클라이언트인 브라우저에서 막는다는 내용이며 응답 헤더에 Access-Control-Allow-Origin 에 들어 있는 URL

swift 용 timer 만들기

순수 스위프트용 타이머 만들기 먼저 타이머 선언을 한다 let total = 0 let timer = DispatchSource.makeTimerSource(queue: DispatchQueue(label: "swiftTimer", attributes: .concurrent)) 이제 타이머를 ViewDidLoad 같은 곳에 설정한다. timer.schedule(deadline: .now(), repeating: .milliseconds(100)) timer.setEventHandler {   self.total += 1   if self.total >= 30 { self.timer.cancel() }   DispatchQueue.main.async {     self.label.text = "\(self.total)"   } } 이제 타이머를 실행 시킬 곳에서 아래와 같이 해야 한다. self.timer.resume()

Server Side Swift 에서 Memory 해제가 안되어 무한 증가할 때 조치 법

몽고디비에 자료를 가져와서 가공을 한 뒤에 다시 몽고디비에 자료를 저장하는 프로그램을 서버용 스위프트로 작성하였다. 하지만 아래와 같이 작성했을 때는 실행을 하면 메모리 해제가 안되어 무한증식하는 상태가 발생되었다. import Foundation import MongoKitten do {     let mongo = try Server("mongodb://localhost:27017")     let db = mongo["db"]     if mongo.isConnected { print("connected successfully to server") }     let collection = db["collection"]     let docu: Document = []     let query = Query(docu)     var textLog = TextLog()     do {         for entity in try collection.find(query) {             trainLocations.forEach {                 textLog.write("\($0.date.Format(into: "yyyy-MM-dd HH:mm:ss Z")) \n")             }         }  //end_for entity in try collection.find(query)     } catch { print("error -> \(error.localizedDescription)") }     try mongo.disconnect() } catch { print("error -> \(error.localizedDescription)") } 이를 해결하기 위해 함수를 포인터를 사용하는 등 별짓을 다했지만 역시나 무한증식은

Server 용 Swift 로 Packet Capture Program 작성하기

현재 스위프트 지원 서버는 우분투만이 가능하다. 센토스도 설정을 맞추면 가능하긴 한데 정신건강을 위해 우분투를 사용하자. Ubuntu 16.04 에서 Swift 를 설치하자 $ sudo apt-get install clang libicu-dev 이제 swift.org 에서 스위프트 압축된 파일을 다운로드 받는다. $ wget -q -0 -https://swift.org/keys/all-keys.asc $ tar xzf swift-<VERSION>-<PLATFORM>.tar.gz 이 명령은 /Home/user 밑에서 실행하여 하위로 푼다. $ export PATH=/Home/user/usr/swift-4.0.3-RELEASE-ubuntu16.04/usr/bin:"${PATH}" 패스를 추가했다. $ swift Welcome to Swift version 4.0.3 어쩌고 나오면 성공한 것이다. 이제 libpcap Library 를 설치해보자. $ sudo apt-get install libpcap-dev 이제 Swift 로 Packet Capture 할 준비가 되었다. Cpcap 이란 폴더를 생성하자. $ mkdir Cpcap $ cd Cpcap 여기서 파일을 2개를 만들것이다. $ touch Package.swift $ touch module.modulemap Package.swift 내용을 이렇다. import PackageDescription let package = Package(name: "Cpcap") module.modulemap 내용을 이렇게 작성한다. module Cpcap [system] {   header "/usr/include/pcap.h"   link "pcap"   export * } 이제 git 을 생성해야 한다. /Pcap 폴더에 위치한 지 확인 후 생성한다.

CryptoSwift 사용하여 암호화 및 복호화 하는 방법

일단 관련 모듈을 사용할 수 있는 환경을 만들자 cocoapods 를 이용하여 설치한다. 해당 프로젝트 터미널에서 $ pod init Pod 파일이 생성된다. pod 'CryptoSwift' 추가한 다음 파일을 저장하고 터미널로 나온 다음에 다음 명령으로 설치를 하자 $ pod install 그럼 해당 프로젝트 폴더에 확장자 .xcworkspace 가 생길 것이다. 이걸 클릭하여 프로젝트를 Xcode 에서 연다. 이제 프레임워크를 열 수 있다. import CryptoSwift 상단에 추가하면 이제 암호화 및 복호화를 할 수 있다. 암호와 복호화에 사용되는 키와 벡터가 있는데 이건 꼭 맞아야 하고 256비트로 하려면 32자 이어야 하고 128비트로 하려면 16자로 정해야 한다. 암호화 방법 do {   let aes = try AES(key: DEFINE_KEY, iv: DEFINE_IV)   let chiperText = try aes.encrypt("암호화할 문자열".bytes).toBase64() } catch { print(error) } 복호화 방법 do {   let aes = try AES(key: DEFINE_KEY, iv: DEFINE_IV)   let e64_data = Data(base64Encoded: chiperText) ?? Data()   let decryptData = try aes.decrypt(e64_data.bytes)   let decryptText = String(bytes: decryptData, encoding: .utf8) } catch { print(error) } 문자열을 찍어보니 암/복호화 잘 된다.

음악 플레이 하는 도중에 앱을 실행하면 중단되지 않도록 하는 방법 조치기

음악이나 라디오 또는 팟캐스트를 들으면서 교대근무 같은 앱을 실행하면 음악이 중단이 된다. 예전에는 아래 코드를 AppDelegate에 넣으면 중단되지 않고 내 앱에서 버튼 효과음까지 같이 실행되었었다. do {     try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)     try AVAudioSession.sharedInstance().setActive(true) } catch { print(error.localizedDescription) } 하지만 앱을 실행하면 중단이 될 뿐이었다. 각종 검색을 해보아도 위와 같은 코드만 나올 뿐이지 답은 없어 보였다. 이 때문에 xcode 도 제거하고 다시 설치 해보았다. 백그라운드 모드를 on 안 시켜서 그렇나? 역시나 마찬가지였다. 혹시나 하여 최근에 만들었던 다른 앱으로 구동해보았다. 음악이 중단되지 않고 버튼 효과음이 나올때 중단되는 것이다. 한줄기 희망을 보았다. 위의 코드를 메인뷰컨트롤러에 넣어 보았다. 역시나 중단이 된다. 혹시나 실행될때 위 코드로 실행을 했더니 중단이 되지 않는다. 각각의 효과음을 위 코드 블럭으로 싸고 실행하였다. 결과는 대성공이다. ㅋㅋㅋ guard let url = Bundle.main.url(forResource: "touch", withExtension: "mp3") else { return } do {     try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)     try AVAudioSession.sharedInstance().setActive(true)     let touchPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp

Swift 용 Realm 설치 후 No such module "Realm" 오류 나올때 조치 방법

Realm 은 모바일 데이터베이스라고 하는데 사용해보니 강력한거 같다. 하지만 강타입으로 모델링을 해야 한다. 그리고 Xcode 에서 pod 으로 설치 한 다음 No such module "Realm" 오류가 자주 난다. 이는 설치된 파일과 연결이 잘 안되어서 나는 오류로 보통 초기화 하면 해결이 된다. 그래도 안되면 아래와 같이 완전 초기화를 해보자 $ pod cache clean Realm $ pod cache clean RealmSwift $ pod deintegrate || rm -rf Pods $ pod install --verbose $ rm -rf ~/Library/Developer/Xcode/DerivedData 이와 같이 하니까 오류가 나지 않는다.

swift 에서 json 파일 parsing 하기

스위프트에서 json 파일을 가져와서 parsing 하기가 어렵지 않다. 일단 json 형식은 다음과 같다. [     "strResult": SUCC,     "r_list": {         "h_dt": 20170824         "t_list": {             "t_info": {                 "h_value": 24             }         }     },     {         "h_dt": 20170821         "t_list": {             "t_info": {                 "h_value": 21             }         }     },     "row_cnt": 2 ] 이제 json 파일을 받아서 처리 할 수 있는 상태로 만들어 보자 do {     let json = try JSONSerialization.jsonObject(with: data, options: .allwFragments) as! [String: Any]     let r_list = json["r_list"] as! [[String: Any]]     let row_cnt = json["row_cnt"] as! string     print("row_cnt --> \(row_cnt)")     // 1. flatMap 사용하여 추출     let t_list = r_list.flatMap { $0["t_list"] as! [[String: Any]] }     let t_info = t_list.flatMap { $0["t_info&quo

Swift 에서 두개의 Array을 순차적으로 합치는 방법

이미지
Swift 에서 두개의 Array 을 합치는 방법으로 각각의 열을 맞춰야 한다. 이럴땐 zip 이란 함수를 사용하면 된다. 위의 방법은 3개의 Array 를 각각의 순서대로 합친 결과이다. 즉 ["홍길동", "전우치", "강감찬"] 과 [3, 7, 6] 과 [2015-08-16, 2016-10-30, 2016-10-31] 의 Array에 각각의 값을 각 1항끼리 합칠 경우에 사용한다. flatMap 은 배열의 배열로 되어 있는 경우를 배열로 만들어 준다.

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. 오류 조치 방법

swift 3 에서 작성하는 도중에 이런 오류가 발생했다. This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread.  대충 내용은 백그라운드에서 메인 스레드를 접속해서 뭔가 안 맞는다는 이야기 같다.  검색 결과 디스패치를 구현할 때 백그라운드에서는 데이터 처리만 그리고 메인은 UI 처리만 하면 된다고 한다. DispatchQueue.global(qos: .background).async { // 오직 데이터 처리만 DispatchQueue.main.async { // 오직 UI } } 위와 같이 처리하니 오류가 안난다.

아이폰 위치정보와 지하철 역과 거리 구하기

서울시에서 제공하는 지하철역과의 거리 구하는 URL 은 좌표가 WTM 방식이다. 하지만 대부분의 스마트폰의 좌표는 WGS84 방식이다. 이를 변환하는 다음 개발자 URL 도 있기는 있지만 이러한 방식으로 구현해서 확인했더니 속도가 나오지 않아서 인터넷에 돌아다니는 소스를 참고 삼아 메소드를 만들었다. // wgs84 좌표 사이의 거리 측정 후 정렬하여 가까운 역 표시 func distanceBetweenCoordinate(lat1: Double, lon1: Double) -> [String] { var distanceStaion:[String:Double] = Dictionary()//[:] // 현재 위치와 subways 좌표와 계산하여 distance.append(code, distance) 넣으면 된다. for subway in subways { let lat2 = subway.latitude let lon2 = subway.longitude let theta = lon1 - lon2 var dist = sin(deg2rad(deg: lat1)) * sin(deg2rad(deg: lat2)) + cos(deg2rad(deg: lat1)) * cos(deg2rad(deg: lat2)) * cos(deg2rad(deg: theta)) dist = acos(dist) dist = rad2deg(rad: dist) dist = dist * 60 * 1.1515 // Kilo Meter dist = dist * 1.609344 distanceStaion[subway.name] = dist } // 계산 끝났으므로 다시 하지 않도록 설정 _isDistance

스위프트 옵셔널 타입

스위프트 옵셔널 타입이 있다. 옵셔널 타입의 목적은 변수 또는 상수에 아무런 타입의 값이 할당되지 않는 상황을 처리하기 위하여 안전하고 일관된 방법을 제공하는 것이다. 옵셔널하게 변수를 선언하고자 하면 선언부 뒤에 '?' 추가하면 된다. var index:Int? 만약 옵셔널이 할당된 값을 갖는다면 그 값은 옵셔널에 래핑되었다고 한다. 옵셔널에 래핑된 값은 강제 언래핑이라 불리는 개념을 사용하여 접근이 가능해진다. 이는 옵셔널 데이터 타입에서 값을 추출한다는 의미이다. 옵셔널 이름 뒤에 '!' 붙이면 된다. 이 방법 말고 옵셔널 바인딩을 이용하여 임시변수 또는 상수에 할당할 수 있다. if let c = option {  } 용어를 어렵게 사용해서 이해가 잘 가지 않는다. 쉽게 말해 스위프트는 선언할 때 값을 될 수 있으면 할당하라는 말이다. 하지만 변수와 상수는 필요할 때 할당하는게 자원의 활용에 좋을 것이다.  lazy 가 대세가 되는 것도 같은 이유이다. 널 값으로 넣어 두고 할당하려면 '?'를 추가하고 이 값을 사용하려면 '!'를 추가해야 한다는 말인거 같다. 정 특수문자를 쓰기 싫다면 if let c = optional {  } 이 구문으로 임시로 할당해서 사용하라는 것 같다. [출처 : 핵심만 배우는 iOS9 프로그래밍, 닐 스미스, 황반석, 2016.4.25, 페이지 54~57]