Things take time

[SWIFT] 마지막 : 스위프트 기본 앱 본문

iOS (교육)

[SWIFT] 마지막 : 스위프트 기본 앱

겸손할 겸 2017. 5. 13. 11:14

** Segue 생성 시 참고


화면에 만든 버튼을 마우스 우클릭 드래그로 뷰 컨트롤러와 연결하면 해당 버튼 클릭시 연결된 뷰 컨트롤러로 이동

뷰 컨트롤러위의 뷰 컨트롤러 버튼을 우클릭 드래그로 뷰 컨트롤러와 연결하면 생성된 세그에 id를 부여하여 해당 id를 통해 이동하는 코드를 작성하면 됨


위의 뷰 컨트롤러는 CREATE ACCOUNT라는 버튼과 연결된 것이고 하단의 뷰 컨트롤러는 뷰컨트롤러 버튼으로 연결된 것이라 오른쪽 상단에 id를 부여했고, 코드로 따로 작성해야 함 (CREATE 버튼은 ACTION으로 연결할 필요 없음)



1. 데이터 저장하기
예제 : 계정 생성 후 UserDefault로 값 저장하기


UserDefaults : Android의 SharedPreference와 같은 기본적인 사용자의 정보를 저장하는 기본 데이터베이스, 앱의 업데이트와 같은 경우에도 데이터는 저장되며, 앱 삭제시에만 삭제 되는 데이터

보통 계정 정보보다는 앱의 버전 값이나 기타 앱의 설정 값을 저장할 때 사용함




Util.swift는 기본 swift파일로 생성하며 싱글톤 객체를 이용한 경고창 만들때 사용

import UIKit

// 싱글톤 만들기, 가장 상위의 NSObject형으로 한다(문법)
class Util:NSObject {
    
    // 싱글톤 객체는 static let을 통해 만든다
    static let sharedInstance = Util()
    
    func showAlert(viewController:UIViewController, title:String, message:String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        viewController.present(alert, animated: true, completion: nil)
    }
    
}


계정 생성 뷰 컨트롤러


import UIKit

class AccountViewController: UIViewController {

    @IBOutlet weak var idTextField: UITextField!
    @IBOutlet weak var pwTextField: UITextField!
    @IBOutlet weak var pwConfirmTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */
    @IBAction func submitClicked(_ sender: UIButton) {
        if idTextField.text?.isEmpty == false &&
            pwTextField.text?.isEmpty == false &&
            pwConfirmTextField.text?.isEmpty == false &&
            pwTextField.text == pwConfirmTextField.text {
            
            // UserDafaults 사용
            // 싱글톤 패턴으로 하나의 객체만 모든 클래스 전역적으로 사용하는 개념
            UserDefaults.standard.set(idTextField.text, forKey: "id")
            UserDefaults.standard.set(pwTextField.text, forKey: "password")
            
            // 이전 페이지 이동
            navigationController?.popViewController(animated: true)
        } else {
            // 싱글톤 객체 호출
            Util.sharedInstance.showAlert(viewController: self, title: "알림", message: "계정 정보가 부족합니다")
        }
    }

}

메인 뷰 컨트롤러

    @IBAction func loginClicked(_ sender: UIButton) {
        print("aaa")
        if idTextField.text?.isEmpty == false && pwTextField.text?.isEmpty == false {
            
            if idTextField.text == UserDefaults.standard.value(forKey: "id") as? String && pwTextField.text == UserDefaults.standard.value(forKey: "password") as? String{
               performSegue(withIdentifier: "LoginSegue", sender: self)
                print("aa")
            }
        } else {
            Util.sharedInstance.showAlert(viewController: self, title: "알림", message: "ID/PW 부분을 모두 입력하세요.")
        }
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? UserViewController {
            vc.userId = idTextField.text!
        }
    }

나머지 : 첨부파일


2. 앱 버전, 이름 정보 가져오기 및 체크하기 

let infoDic = Bundle.main.infoDictionary! let appName = infoDic["CFBundleName"] as! String let appVersion = infoDic["CFBundleShortVersionString"] as! String let appBuild = infoDic["CFBundleVersion"] as! String print("appName = \(appName)") print("appVersion = \(appVersion)") print("appBuild - \(appBuild)")

override func viewDidLoad() { super.viewDidLoad() if let savedVersion = UserDefaults.standard.value(forKey: "appVersion") as? String { let newVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String if savedVersion != newVersion { Util.sharedInstance.showAlert(viewController: self, title: "알림", message: "앱 업데이트") saveCurrentVersion() } } else { Util.sharedInstance.showAlert(viewController: self, title: "알림", message: "최초 실행(앱 삭제 후, 혹은 설치 후 최초 실행)") saveCurrentVersion() } } func saveCurrentVersion(){ let currentVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String UserDefaults.standard.set(currentVer, forKey: "appVersion") }

3. 키체인

앱과 다른 앱사이의 통신을 위한 데이터로 앱을 삭제하더라도 데이터가 유지되는 특징이 있음 (같은 팀으로 개발된 다른 앱을 의미함, 모든 앱을 의미하는게 아님)

테스트할 때는 Capabilities 의 Keychain Sharing을 On하고 사용할 앱 정보와 공유할 앱의 패키지 명을 입력


테스트

하나의 앱에서 키체인 값 저장 및 가져오기

import UIKit
// 키 체인 사용시 import
import Security

// 높은 보안 수준의 값 저장이 필요한 경우 사용
// 키체인 그룹 설정을 통해 앱간 설정값 공유가 가능함
class Keychain {
    
    class func save(key: String, data: Data) -> Bool {
        let query = [
            kSecClass as String       : kSecClassGenericPassword as String,
            kSecAttrAccount as String : key,
            kSecValueData as String   : data ] as [String : Any]
        
        SecItemDelete(query as CFDictionary)
        
        let status: OSStatus = SecItemAdd(query as CFDictionary, nil)
        
        return status == noErr
    }
    
    class func load(key: String) -> Data? {
        let query = [
            kSecClass as String       : kSecClassGenericPassword,
            kSecAttrAccount as String : key,
            kSecReturnData as String  : kCFBooleanTrue,
            kSecMatchLimit as String  : kSecMatchLimitOne ] as [String : Any]
        
        var dataTypeRef: AnyObject?
        let status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
        
        if status == errSecSuccess {
            if let data = dataTypeRef as! Data? {
                return data
            }
        }
        return nil
    }
    
    class func delete(key: String) -> Bool {
        let query = [
            kSecClass as String       : kSecClassGenericPassword,
            kSecAttrAccount as String : key ] as [String : Any]
        
        let status: OSStatus = SecItemDelete(query as CFDictionary)
        
        return status == noErr
    }
    
    
    class func clear() -> Bool {
        let query = [ kSecClass as String : kSecClassGenericPassword ]
        
        let status: OSStatus = SecItemDelete(query as CFDictionary)
        
        return status == noErr
    }
    
}
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        /* 처음 실행
        // 테스트용 계정
        let id = "schidsi"
        let password = "1234abcd"
        
        // 키체인 저장 -> print : true true
        if let idData = id.data(using: String.Encoding.utf8),
            let passwordData = password.data(using: String.Encoding.utf8) {
                print(Keychain.save(key: "id", data: idData))
                print(Keychain.save(key: "password", data: passwordData))
        }
        */
        
        
        // 처음 실행 후 처음 실행 부분 주석하고 실행
        // 키체인에서 데이터 가져오기 -> print -> id : schidsi / pw : 1234abcd
        if let idData = Keychain.load(key: "id"), let passwordData = Keychain.load(key: "password"){
            if let id = String(data: idData, encoding: .utf8), let password = String(data: passwordData, encoding: .utf8){
                print("id : \(id) / pw : \(password)")
            }
        }
    }   
}


공유할 앱에서는 아래와 같이 사용 : 팀, 개발 타겟 등을 맞춰주고 Capabilities의 keyChain Sharing을 On한 뒤, 공유할 앱들의 리스트를 생성한 패키지 포함하여 입력

KeyChain.swift를 넣고, 위의 예제에서 키체인에서 데이터 가져오는 소스를 그대로 복사하여 viewDidLoad()에서 하면, 같은 결과 값을 얻어올 수 있음
키체인의 경우 앱이 삭제되어도 키체인에서 저장이 되기때문에, 유지가 되나 OS를 새로 설치할 경우는 날아가게 됨, 이런 경우 또 다른 저장 방법을 생각할 수 있는데 그 것은 바로 서버에 저장하는 경우


4. Firebase를 이용한 데이터 베이스 이용하기 : https://firebase.google.com/docs/ & https://firebase.google.com/docs/database/ios/start

설명 그대로 하면 간단함, FCM 하듯이..


5. Framework 생성 후 다른 프로젝트에서 가져다 쓰기

프레임워크 생성시엔 Cocoa Touch Framework로 생성후 코드 작성 후 Products 밑에 있는 Framework파일을 사용할 프로젝트에서 General의 Embeded Binaries에 드래그하여 가져다 쓰면 됨 -> 안될 경우 해당 프레임워크를 Show Finder로 열어 복사 후, 옮길 프로젝트에 붙여넣기로 넣은 뒤에 드래그


그리고 사용할 때는 import로 해당 프레임워크명 입력하고 가져다 씀 : Util.framework라는 프레임워크를 넣고 사용한 화면



6. Object-C -> Swift에서 사용하기

.h와 .m파일을 넣었다면, 팝업창으로 Configure관련 메시지가 뜨면 브릿지를 연결하겠다는 버튼을 클릭하고, 브릿지 헤더.h 파일에 아래처럼 입력 (해당 헤더파일만 include)

// // Use this file to import your target's public headers that you would like to expose to Swift. // #include "DLRadiobutton.h"

만약 .h, .m파일을 넣지 않고 해당 파일이 있는 폴더 자체를 넣으면 브릿지 관련 메시지가 뜨지 않음, 이럴 때는 헤더파일을 따로 생성하고 Build Settings -> Swift Compiler - General의 Object-C Bridgind Header의 해당 파일을 알려줘야함