Emacs – Upgrade new emacs-live version.

기록하는 습관을 들이자.

emacs-live가 ~/.emacs.d/에 설치 되어있는지 확인하고, 만약 확인이 된다면 아래의 명령어를 입력해준다. 단 이때 git 저장소로부터 다운받아 설치가 되어 있어야 한다.

$ cd ~/.emacs.d/
$ git pull

이상.

Emacs – 프로젝트 생성

기존 프로젝트 의존성이 맞지 않아 오류가 발생된다.

새로 프로젝트를 생성한다면 아래의 설정대로 만들어주어야 한다.

기존 버전으로 사용할 경우 아래의 문제가 발생된다. midje 사용하고, lein repl을 사용했을 경우 cider쪽에서 some-> 함수를 찾지 못하는 문제가 발생한다.

오류를 줄이기 위한 가장 좋은 방법은 최신버전을 사용하는 것이다. (물론 최신버전에 또다른 오류가 발생될 수 있으나, 의존성 처리를 미리미리 해두는게 좋다.)

Global로 설정된 버전들 – cider-nrepl “0.9.1” – clojure “1.4.0”

최신 버전 – cider-nrepl “0.13.0” – clojure “1.8.0”

:dependencies [[cider/cider-nrepl "0.13.0"]]
:repl-options {:nrepl-middleware
                 [cider.nrepl.middleware.apropos/wrap-apropos
                  cider.nrepl.middleware.classpath/wrap-classpath
                  cider.nrepl.middleware.complete/wrap-complete
                  cider.nrepl.middleware.debug/wrap-debug
                  cider.nrepl.middleware.format/wrap-format
                  cider.nrepl.middleware.info/wrap-info
                  cider.nrepl.middleware.inspect/wrap-inspect
                  cider.nrepl.middleware.macroexpand/wrap-macroexpand
                  cider.nrepl.middleware.ns/wrap-ns
                  cider.nrepl.middleware.pprint/wrap-pprint
                  cider.nrepl.middleware.pprint/wrap-pprint-fn
                  cider.nrepl.middleware.refresh/wrap-refresh
                  cider.nrepl.middleware.resource/wrap-resource
                  cider.nrepl.middleware.stacktrace/wrap-stacktrace
                  cider.nrepl.middleware.test/wrap-test
                  cider.nrepl.middleware.trace/wrap-trace
                  cider.nrepl.middleware.out/wrap-out
                  cider.nrepl.middleware.undef/wrap-undef
                  cider.nrepl.middleware.version/wrap-version]}

위 내용을 정리하는 이유는 Midje와 cider 버전 차이가 맞지 않아 발생되는 문제로, lein과 사용되는 lein midje :autotest를 사용할 경우 아무 문제 없다. 하지만 문서상 lein midje ****보다는 lein repl에서 autotest명령을 수행함으로 써 하는 방식을 더 권장하고 있다.

사용해보니 왜 그렇게 권장하는지 알 수 있다.

$ lein repl
$ (use 'midje.repl)
$ (autotest) 

Google Apps Script – 파일 업로드하기

Google Forms은 파일 업로드를 지원하지 않습니다. 하지만 Google Apps Script와 HTML 웹 폼을 사용한다면, Google Drive에 파일을 업로드 할 수 있습니다.

예를 들어, 선생님이 학생들에게 레포트를 받기 위해 웹 폼을 작성하고, 작성 한 폼을 공유함으로써, 학생들은 그 웹폼에 접근하여 파일을 업로드 할 수 있습니다.

Google Script는 업로드하는 파일을 blob 타입으로 읽고 구글드라이브에 새로운 파일로 저장하게 됩니다. 파일이름, 확장자 컨텐츠 내용까지 다 지정할 수 있습니다.

Google에서 제공되는 Google Script를 사용해보도록 하겠습니다.

파일 구조는 두가지 파일로 (Code.gs, form.html)으로 구성됩니다. Code.gs – Google Apps Script입니다. 다양한 Google API를 사용할때 사용됩니다. form.html – 학생들에게 보이는 화면입니다. HTML,CSS, 그리고 JavaScript로 구성되어 있습니다.

아래와 같이 작성해주세요.

Code.gs

/* The script is deployed as a web app and renders the form */
function doGet(e) {
  return HtmlService.createHtmlOutputFromFile('form.html').setSandboxMode(HtmlService.SandboxMode.NATIVE);
  // This is important as file upload fail in IFRAME Sandbox mode.
}
 
/* This function will process the submitted form */
function uploadFiles(form) {
  
  try {
    
    /* Name of the Drive folder where the files should be saved */
    var dropbox = form.myName + " " + form.myEmail;
    var folder, folders = DriveApp.getFoldersByName(dropbox);
    
    /* Find the folder, create if the folder does not exist */
    if (folders.hasNext()) {
      folder = folders.next();
    } else {
      folder = DriveApp.createFolder(dropbox);
    }
    
    /* Get the file uploaded though the form as a blob */
    var blob = form.myFile;    
    var file = folder.createFile(blob);    
    
    /* Set the file description as the name of the uploader */
    file.setDescription("Uploaded by " + form.myName);
        
    /* Return the download URL of the file once its on Google Drive */
    return "File uploaded successfully " + file.getUrl();
    
  } catch (error) {
    
    /* If there's an error, show the error message */
    return error.toString();
  }
  
}

form.html

<!-- Include the Google CSS package -->
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
 
<!-- You can also include your own CSS styles -->
<style>
  form { margin: 40px auto; }
  input { display:inline-block; margin: 20px; }
</style>
 
<script>
  // The function will be called after the form is submitted
  function doUploadFile() {
    document.getElementById('uploadFile').value = "Uploading File..";
    var f = document.getElementById('labnol');
    google.script.run.withSuccessHandler(fileUploaded).uploadFiles(f);
    return false;
  }
 
  // This function will be called after the Google Script has executed
  function fileUploaded(status) {
    document.getElementById('labnol').style.display = 'none';
    document.getElementById('output').innerHTML = status;
  }
  
</script>
 
<!-- This is the HTML form -->
<form id="labnol">
 
  <!-- Text input fields -->
  <input type="text" name="myName" placeholder="Your name..">
  <input type="email" name="myEmail" placeholder="Your email..">
 
  <!-- File input filed -->
  <input type="file" name="myFile">
 
  <!-- The submit button. It calls the server side function uploadfiles() on click -->
  <input type="submit" id="uploadFile" value="Upload File" 
         onclick="this.value='Uploading..';doUploadFile();">
 
</form>
 
<!-- Here the results of the form submission will be displayed -->
<div id="output"></div>

위 코드를 작성후 프로젝트 명을 지정해주세요. 이전에 지정되어도 상관없습니다. 프로젝트 명을 작성할 경우 지정된 이름이 drive> 아래 놓이게 됩니다.

프로젝트명은 ‘report’로 하겠습니다.

프로젝트가 생성되고, 위 파일을 성공적으로 작성하였다면, ‘Publish > Deploy as web app’을 선택해주세요.

Project version: new (새로 만들었다면, 기존것을 변경 할 수도 있습니다.) Execute the app as: Me (앱이 실행되기 위해 권한을 부여받기 위한 사용자가 되겠습니다. ) Who has access to the app: Anyone, even anonymous 누가 이 웹에 접속 할 수 있는지, 접근 권한을 선택 할 수 있습니다.

모든 설정을 완료하고 업데이트를 누르면, 공유 URL이 생성됩니다.

이상.

참조

이맥스 – 루비 제작자 “맷츠”, 어떻게 이맥스가 내 인생을 바꿧나?

원문

  1. 이맥스가 내 인생을 어떻게 바꿧나? “Yukihiro Matsumoto” – Matz @yukihiro_matz

  2. 1980

  3. 프로그래밍을 시작했다.

  4. ~

  5. BASIC

  6. 400 단계

  7. 1988

  8. 이맥스와의 만남

  9. Sun-3 위에서

  10. 200명 학부생들과 공유

  11. 이맥스를 시도했었다.

  12. 하지만 사용하지는 않았다.

  13. 이맥스는 금지 되어었다.

  14. 이맥스는 너무 많고 소중한 메모리가 소비하였다.

  15. 우리는 무료 소프트웨어를 받을 수 있는 자유가 있었다.

  16. 우리는 소스코드를 읽을 수 있는 자유가 있었다.

  17. 난 이맥스 소스코드를 다운받았다.

  18. 그리고 이를 연구하였다.

  19. 이맥스는 나의 첫번째 리스프 인터프리터였고,

  20. 나는 이맥스로부터 많은 언어에 대해서 배울 수 있었다.

  21. 포인터들 안에서 내장되어 있는 정수들 (?)

  22. 가비지 컬렉션을 청소했다.

  23. 리스프와 ‘C’ 사이에서의 대회로 불리었고

  24. 나는 리스프가 어떻게 동작하는지 이해할 수 있었다.

  25. 나는 리스프 오브텍트에 매료되었고

  26. 리스프 오브젝트는 C로부터 선언되었다. (?)

  27. 그리고 나는 Sparc Station을 얻었다. (SPARCstation(aka Sun-4) 시리즈는 1989년에 소개되었다.)

  28. 나는 이맥스를 사용하기 위해 다시 시작했고

  29. 이맥스는 나의 한부분이 되었다.

  30. 만약 이맥스에서 아무것도 좋아하지 않았다면, 나는 이것들을 바꿀수가 있었다.

  31. 이맥스는 완전하게 개조가 가능하였다.

  32. 이맥스는 프로그래머가 원하는 모든것을 바꿀 수 있다는걸 깨닫게 해주었으며,

  33. 이 모든것은 자유였다.

  34. 카바인딩 생각없이 모든 것을 바꿀 수 있었다. (?)

  35. 나는 이맥스 없이 아무것도 쓰고 싶지 않았다.

  36. 프로그램들, 문서 그리고 메일들까지

  37. 그래서 나는 내 전용 메일 클라이언트로 썼다.

  38. 이름하여 “email”

  39. 이맥스 리스프 안에서

  40. 이게 나의 첫번째 보잘거 없지 않은 리스프 프로그램이였다.

  41. 나는 매일 이것을 사용했다.

  42. 1993

  43. 나는 루비 개발을 시작했다.

  44. 이맥스 구현으로의 영향력은

  45. 태그 포인터들안에서 인티저는 코드였고,

  46. 이것은 간단한 영역과 가비지 컬렉션을 소비하여 사용하였다.

  47. 비슷한 오브젝트 모델에서 리스프로 사용했고 (여기서 리스프의 오브젝트의 “object”란 내부적으로 표현되 코드이고, OOP를 말하는 것이 아니다.)

  48. 그리고 나는 맨위에 OO 시스템과 같은 스몰토크를 넣었고

  49. 문법적으로 ALGOL/Ada/Eiffel들을 원했다.

  50. 하지만 이맥스 중독자로, 나는 언어 모드가 필요했었고,

  51. auto-indent는 필요한 것이었다.

  52. 1993년으로 돌아가, 문법이 표현된 언어를 위한 auto-indenting 언어 모드는 없었고

  53. 그래서 나는 ruby-mode.el를 만들었다.

  54. 이맥스 리스프와 정규식 표현을 다뤄야만 했고

  55. 매주 그렇게

  56. auto-indentation구현을 어떻게든 성공했다.

  57. 언어의 “end”를 위해

  58. 만약 진행하기 위한 ruby-mode를 만들지 않았다면,

  59. 루비의 문법은 바뀌었을지도 모른다.

  60. C와 같은 또다른 하나

  61. 다른 스크립트언어와 너무 비슷한

  62. 결과적으로 루비 현재의 인기를 차지하지 못햇을 것이다.

  63. 개요

  64. 1) 이맥스는 소프트웨어에 대한 자유를 가르쳐주었다.

  65. 2) 이맥스는 코드를 어떻게 읽는지 가르쳐주었다.

  66. 3) 이맥스는 리스프의 힘을 가르쳐 주었다.

  67. 4) 이맥스는 언어의 핵심이 어떻게 구현되는지 가르쳐 주었다.

  68. 5) 이맥스는 가비지 컬렉션이 어떻게 구현되는지 가르쳐 주었다.

  69. 6) 이맥스는 코드와 디버그를 도와주었다.

  70. 7) 이맥는 텍스트, 메일, 문서 작성과 편집을 도와주었다.

  71. 8) 이맥스는 효과적인 프로그래머가 될수 있도록 도와주었다.

  72. 9) 이맥스는 나를 해커로 만들어 주었다.

  73. 10) 이맥는 내 인생을 바꿔주었다.

  74. 영원히

  75. 감사합니다.

이맥스 – 텍스트 정렬하기

날씨 탓일까요? 뭔가 막혀있는 느낌이 자꾸 드네요. 이럴 땐 역시나 무엇인가에 몰두하는게 가장 좋더라고요.

이맥스 – 텍스트 정렬하기

이건 왜 몰랐을까? 아래와 같이 정렬되지 않은 텍스트를 일정한 규칙에 의해서 정렬할때 align-regexp기능을 사용할 수 있습니다.

rooney = 21
haha = 20
ray = 17

만약 문자 “=” 을 기준으로 정렬을 하고 싶다면, align-regexp을 치고 문자로 “=”을 입력해주세요.

결과는 아래와 같이 정렬된 것을 볼 수 있습니다.

rooney = 21
haha   = 20
ray    = 17

약간 고급스럽게 아래와 같이 사용 할 수 있습니다.

California 423,970 km²
Taiwan 36,008 km²
Japan 377,944 km²
Germany 357,021 km²
Iraq 438,317 km²
Iran 1,648,195 km²
Korea (North+South) 219,140 km²
Mexico 1,964,375 km²

위에 것을 아래와 같이

Taiwan                 36,008 km²
Korea (North+South)   219,140 km²
Japan                 377,944 km²
Germany               357,021 km²
California            423,970 km²
Iraq                  438,317 km²
Iran                1,648,195 km²
Mexico              1,964,375 km²

먼저 전체 텍스트를 영역 지정하고, CTRL+u 그리고 나서 align-regex명령을 입력합니다. 정규식 입력 란에

.* \([0-9,]+\).*

을 입력하고, 그룹에 대해서는

-1

, 공백은

1

을 입력합니다. 반복 실행은

n

을 입력해줍니다.

위와 동일한 효과를 가질 수 있는 두가지 명렁어가 더 있습니다.

  • sort-regexp-fields
  • sort-columns

급하게 잡힌 약속에 나가봐야해서 이만 쓰겠습니다.

Tool – 맥용 최강 터미널 툴 iTerm2!!!

iTerm2는 많은 기능들을 제공합니다.
터미널 사용자들은 미리 볼수 있고 해결한 수 있는 많은것들을 상상할 수 잇습니다. 그리고 지금 보는 것들은 아주 매력적인 것들입니다.

분활 화면

여러개화면을 각각 탭으로 나누는것과, 다른 세션을 한 화면에서 보여주는 기능을 제공합니다. 또한 세로든 가로든 화면을 나눌수 있고 패널에 번호를 매길수 있습니다.

NewImage

사용하지 않는 화면들은 딤처리 되어 현재 활동하고 있는 구간을 쉽게 확인 할 수 있습니다.

윈도우 핫키

단축키를 등록함으로써 iTerm2를 다른 어플리케이션 사이에서 화면 맨앞쪽으로 쉽게 불러올 수 있습니다. 또한 핫키를 통해서 아주 자주 사용하던 창을 선택 할 수 있습니다. 이 기능은 마우스로 손이 안가게끔 시간을 단축하게 해주죠.

NewImage

검색

iTerm2은 한페이지에서 검색할 수 있는 강력한 기능을 제공합니다. 화면에서 보이지는 않는 것까지 동일한것들을 찾아주고 하이라이트로 표시해줍니다. 또한 Regular Expression도 제공하지요.

NewImage

자동완성

간단하게 입력하고 필요할때

Cmd-;

을 입력하시면 자동 완성 창이 활성화 됩니다. 그리고 친적이 있고 이미 분석되어 리스트에 있는 항목들을 보여주죠.

NewImage

마우스 없이 복사하기

검색 명령어로 원하는 키워드를 검색하고, 탭 또는 시프트 탭을 활용하여 왼쪽 오른쪽 영역을 선택하세요. 그리고 원하는 영역이 지정되고 그 영역을 복사하고 싶을때는

Opt-Enter

를 눌러주세요.

NewImage

붙여 넣기 히스토리

붙여 넣기 히스토리는 재방문, 그러니까 최근에 사용했던 내용을 다시 사용하고 싶을 때 사용합니다. 목록에서 또한 선택이 가능하고 절대 읽어버릴일이 없을 겁니다.

NewImage

Instant Replay? 번역을 어떻게?

Instant replay 기능은 시간을 되돌릴수 있는 기능입니다.

NewImage

… 더 많은 내용은 아래의 링크로

http://iterm2.com/features.html

iOS – 음성 바꾸기

원문

필독
… 생략

iOS 기능 중 텍스트를 음성으로 내주는 기능있습니다. 정말 강력한 기능이고, 또한 남자 또는 여자 목소리 또한 선택이 가능합니다.

아래에 보시면 언어에 따른 각 다른 목소리를 골라 사용할 수 있습니다.

[AVSpeechSynthesisVoice 0x17e30940] Language: ar-SA, Name: Maged, Quality: Default, 
[AVSpeechSynthesisVoice 0x17d3e150] Language: cs-CZ, Name: Zuzana, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e79700] Language: da-DK, Name: Sara, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e50c50] Language: de-DE, Name: Anna, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ec30f0] Language: el-GR, Name: Melina, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e687f0] Language: en-AU, Name: Karen, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e23be0] Language: en-GB, Name: Daniel, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e6ffc0] Language: en-IE, Name: Moira, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ec79b0] Language: en-US, Name: Samantha (Enhanced), Quality: Enhanced, 
[AVSpeechSynthesisVoice 0x17e795a0] Language: en-US, Name: Samantha, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e75900] Language: en-ZA, Name: Tessa, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e27ec0] Language: es-ES, Name: Monica, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ec5e10] Language: es-MX, Name: Paulina, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e799c0] Language: fi-FI, Name: Satu, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ec61c0] Language: fr-CA, Name: Amelie, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e75b10] Language: fr-FR, Name: Thomas, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ee2460] Language: he-IL, Name: Carmit, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ef5140] Language: hi-IN, Name: Lekha, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e6f840] Language: hu-HU, Name: Mariska, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e59bd0] Language: id-ID, Name: Damayanti, Quality: Default, 
[AVSpeechSynthesisVoice 0x17d2c950] Language: it-IT, Name: Alice, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ebaec0] Language: ja-JP, Name: Kyoko, Quality: Default, 
[AVSpeechSynthesisVoice 0x17d42100] Language: ko-KR, Name: Yuna, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ee3130] Language: nl-BE, Name: Ellen, Quality: Default, 
[AVSpeechSynthesisVoice 0x17d637d0] Language: nl-NL, Name: Xander, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e7c8b0] Language: no-NO, Name: Nora, Quality: Default, 
[AVSpeechSynthesisVoice 0x17d3e430] Language: pl-PL, Name: Zosia, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e33190] Language: pt-BR, Name: Luciana, Quality: Default, 
[AVSpeechSynthesisVoice 0x17eabf00] Language: pt-PT, Name: Joana, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e2a650] Language: ro-RO, Name: Ioana, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e232b0] Language: ru-RU, Name: Milena, Quality: Default, 
[AVSpeechSynthesisVoice 0x17ee6830] Language: sk-SK, Name: Laura, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e64a10] Language: sv-SE, Name: Alva, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e62fc0] Language: th-TH, Name: Kanya, Quality: Default, 
[AVSpeechSynthesisVoice 0x17df4080] Language: tr-TR, Name: Yelda, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e76a10] Language: zh-CN, Name: Ting-Ting (Enhanced), Quality: Enhanced, 
[AVSpeechSynthesisVoice 0x17db4850] Language: zh-CN, Name: Ting-Ting, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e2ecf0] Language: zh-HK, Name: Sin-Ji, Quality: Default, 
[AVSpeechSynthesisVoice 0x17e224c0] Language: zh-TW, Name: Mei-Jia, Quality: Default]

위 코드에 맞는 언어/나라

Arabic (Saudi Arabia) - ar-SA
Chinese (China) - zh-CN
Chinese (Hong Kong SAR China) - zh-HK
Chinese (Taiwan) - zh-TW
Czech (Czech Republic) - cs-CZ
Danish (Denmark) - da-DK
Dutch (Belgium) - nl-BE
Dutch (Netherlands) - nl-NL
English (Australia) - en-AU
English (Ireland) - en-IE
English (South Africa) - en-ZA
English (United Kingdom) - en-GB
English (United States) - en-US
Finnish (Finland) - fi-FI
French (Canada) - fr-CA
French (France) - fr-FR
German (Germany) - de-DE
Greek (Greece) - el-GR
Hindi (India) - hi-IN
Hungarian (Hungary) - hu-HU
Indonesian (Indonesia) - id-ID
Italian (Italy) - it-IT
Japanese (Japan) - ja-JP
Korean (South Korea) - ko-KR
Norwegian (Norway) - no-NO
Polish (Poland) - pl-PL
Portuguese (Brazil) - pt-BR
Portuguese (Portugal) - pt-PT
Romanian (Romania) - ro-RO
Russian (Russia) - ru-RU
Slovak (Slovakia) - sk-SK
Spanish (Mexico) - es-MX
Spanish (Spain) - es-ES
Swedish (Sweden) - sv-SE
Thai (Thailand) - th-TH
Turkish (Turkey) - tr-TR

실제로 사용 하는 방법을 알아보겠습니다.

let voice = AVSpeechSynthesisVoice(identifier: AVSpeechSynthesisVoiceIdentifierAlex)

다른 목소리를 사용하기 위해서, 이름으로 매칭하여 사용 할 수 있습니다. [code]identifier[/code]으로는 매치가 안됩니다.

목록 불러오기

var voiceToUse: AVSpeechSynthesisVoice?
for voice in AVSpeechSynthesisVoice.speechVoices() {
    if #available(iOS 9.0, *) {
        if voice.name == "Karen" {
        voiceToUse = voice
        }
    } 
} 

텍스트를 주어 음성을 내는 방법은 아래와 같습니다.

let utterance = AVSpeechUtterance(string: "Hello from iOS.")
utterance.voice = voiceToUse
let synth = AVSpeechSynthesizer()
synth.speakUtterance(utterance)

iOS – 유닛 테스트 쉽게 하기! (Swift 2 + XCode 7)

원문

유닛 테스트를 하기 위해 아래의 항목을 확인해주어야 한다.

1. 내부 클래스 만들기

만약 새롭게 내부 클래스를 만들었다면, 클래스나 메서드를 찾지 못한다면, 아래와 같이 Public 처리를 해주어야 한다.

NewImage

2. Import with @testable

테스트 타겟에서, 아래와 같이 @testable keyword를 사용하여, 모듈을 import해줘야한다.

NewImage

3. Test하기

모든게 완료되었다. 외부 클래스나 메서드도 쉽게 테스트 할 수 있다.

NewImage

끝.

iOS 9 – 새로운 UIAlertController, Swift Closures and Enumeration

원본 자료

iOS 8 SDK가 나오면서, 그렇게 큰 관심을 두가지 변화 점에 대해서 설명하고자 한다.
이전에는 UIActionSheetUIAlertView가 사용되었다면, 이제는 UIAlertController라는 클래스를 대체 사용하여야 한다.

iOS 8에서, 앱 내 Alert 메시지를 표현하고 싶다면, 위에서 언급한 UIAlertSheetUIAlertView가 아닌 UIAlertController를 사용하여야 한다. UIAlertController내 Enum값으로 두가지 스타일을 제공한다. 스타일 설정으로 우리는 UIAlertSheetUIAlertView를 선택 할 수 있다. 그냥 둘중에 하나만 선택하며 되는것이다. 또한 우리가 액션을 등록할 때 ex)UIAlertViewDelegate를 사용하였다면, 이제는 closures나, Objective-C에서의 블럭 코드를 사용해야 한다.


이 소개 페이지에서는 UIAlertConroller 클래스 사용법과 alert message표현 방법과 action sheet에 대해서 설명할 것이다.

NewImage

자 시작해보자.

새로운 Xcode 프로젝트 생성

데모하기에는 새로운 프로젝트를 생성하는 방법이 가장 좋다. 자 Single View Application 템플릿을 선택하고, UIAlertDemo라는 이름으로 프로젝트를 생성해보자.

!! Swift Language 선택하는 것을 잊지 말도록!.

여기서는 Xcode 6 Beta를 사용했다는 것을 염두해 두길 바란다. (번역자 데모 실행 7버전도 정상적으로 동작함)

Xcode 6에서는 기본적으로 Size Classes가 활성화 된다.
!! Size Classes기능은 모든 화면을 자동으로 수용하는 기능이다.

여기서는 간단하게 Size Classes 기능을 비활성화 하는 것으로 한다. Main.storyboard에 File inspector옵션에서 “Use Size Classes”기능을 체크를 해지하자. 체크를 해지하게 되면 Device Type선택 창이 보여질 것이고. 우리는 iPhone을 선택 할 것이다.

화면은 아래와 같이 볼 수 있을 것이며, 간단한 버튼 컴퍼넌트를 드래그 하여 넣어주자. 제목은 간단하게 “Hello Alert”로 넣어주자.

NewImage

Action 메서드 추가하기

ViewController.swift에 아래와 같이 코드를 작성해주자.이는 나중에 “Hello World” 버튼을 클릭했을 때 취해지는 액션 메서드이다. 추후 스토리보드에서 버튼과 저 메서드를 따로 연결 시켜주어야 한다.

@IBAction func showAlert() {

}

알다시피, OutletAction은 스토리보드에서 소스코드와 UI Object를 연결하게 해준다.

Swift에서는 메서드 앞에 @IBAction 붙여주면 된다.

자 그러면 이제 실제로 메서드와 UI Object를 연결해보자.

Document Outline에서, CTRL을 누르고, View Controller에을 클릭 후 “Hello Alert”까지 드래그 해주자 그리고 “showAlert”를 선택해주자.

아래의 그림과 같이 될 것이다.

NewImage

실제 Alert를 표현하기 위해 UIAlertViewController를 사용하기

거의 모든 설정이 완료되었다. 이번에는 간단하게 alert message를 표현하는 가장 기본이 되는 것을 설명할 것이다. 간단하게 표현하기 위해 기초가 되는 것은 아래와 같다.

  • UIAlertController 객체 생성! (title, message, alert controller의 스타일 선택)

  • UIAlertAction 생성과 위에서 생성한 alert controller에 반인딩!

  • 마지막으로, presentViewController 메서드를 호출함으로써 alert controller 을 보여주기!

우리는 swift를 사용하고, 위 룰에 충족 시킨다면 코드는 아래와 같이 나올 것이다.


@IBAction func showAlert() {
        let alertController = UIAlertController(title: "Hey AppCoda", message: "What do you want to do?", preferredStyle: .Alert)
        
        let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
        alertController.addAction(defaultAction)
        
        presentViewController(alertController, animated: true, completion: nil)
}

간단하죠? alert나 action sheet를 표현하는 방법은 위와 동일합니다. 단지 스타일만 선택해주면 된다. .Alert를 선택한다면, alert message를 보여주고, .ActionSheet 스타일을 지정한다면, action sheet가 표현될것이다.

기본적으로, alert controller는 아무 액션도 포함하고 있지 않다. 만약, 어떠한 액션을 추가하고 싶다면, 예로 alert의 버튼 액션이 취해졌을때 사라지게 하고 싶다면, 우리는 UIAlertAction 객체를 생성하고 addAction메서드를 활용하여, alert controller에 연결시켜주어야 한다.

UIAlertAction을 생성할때 두가지 파라미터를 받는다. 제목과 핸들러이다. 핸드로는 블럭 코드로, 버튼이 눌려졌을 때 실행되는 부분이며, handler에 nil을 포함하게 되면, controller는 사라지는 기능만 가지게 될것이다. 이부분에 대해서 나중에 더 이야기 하도록 하겠다.

마지막으로, presentViewController메서드를 호출함으로써 alert controller 를 표현할 것이다. 만약 앱을 실행하고, 버튼을 클릭하게 되면 아래와 같이 실행될 것이다.

NewImage

Swift에서 Enumeration에 대한 설명

Swift코드 작성이 처음이라면, .Alert와 .ActionSheet이 생소할 것이다. 그 부분같은 경우 아래와 같이 full syntax로 사용할 수 있다.

let alertController = UIAlertController(title: "Hey AppCoda", message: "What do you want to do?", preferredStyle: UIAlertControllerStyle.Alert)

위 코드는 우리가 사용했던 부분과 완전히 동일한 방법이고, 사실 UIAlertControllerStyle은 아래와 같이 enumration이다.

enum UIAlertControllerStyle : Int { 
     case ActionSheet 
     case Alert 
}

swift에서 enumeration은 값들을 다 그룹화 시킬때 사용된다. AlertSheet와 Alert는 enumeration 안에 멤버 값으로 정의된다. objective C와 같지 않게 만들때 상수는 아니다. AcitonShee와 Alert는 0 그리고 1로 정의되지는 않는다. 각 각 멤버들은 enumberation에서의 full-fledged 값을 가진다.

쉽게 말하면, 딱 정해진 값은 아니지만, 그 안에서는 독립된 값을 가지게 된다.

UIAlertControllerStyle.Alert로 사용할 수 도 있지만 .Alert를 사용하도록 하자.

당신의 짧은 코드는 당신이 더 쉽게 읽을 수 있도록 한다.

Action Handler와 Closures

UIAlertController로 돌아가보자. 우리가 이야기 아직 이야기 하지 않은 것중 하나, UIAlertAciton의 handler이다. 우리가 UIAlertAction 객체를 생성할때 우리는 handler에 블럭 코드를 작성하기 되는데, 이 블럭 코드는 사용자로부터 이벤트를 받았을 때 실행되는 코드이다.

showAlert 메서드 안에 아래와 같이 코드를 작성하고 다른 액션을 취하도록 해보자.

let callActionHandler = { (action:UIAlertAction!) -> Void in
            let alertMessage = UIAlertController(title: "Service Unavailable", message: "Sorry, the call feature is not available yet. Please retry later.", preferredStyle: .Alert)
            alertMessage.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            self.presentViewController(alertMessage, animated: true, completion: nil)
        }
        let callAction = UIAlertAction(title: "Call", style: .Default, handler: callActionHandler)
        alertController.addAction(callAction)

우리는 새로운 handler를 정의하고 이를 추가하였다. 위 코드는 간단하게 alert 버튼이 눌려졌을 때 취하는 액션을 정의하고 추가만 해줬을 뿐이다. 블럭코드는 Closure로 알려져 있다.

Closures는 코드 자체를 기능으로 할 수 있는 블럭을 자체적으로 포함하고 있다. 이것은 위와 같이 Objective-C에 블럭과 굉장히 흡사하다.

action closure에 값 같은 것을 제공하기 위한 한가지 방법은 코드 블럭과 같은 상수 또는 변수이다.

코드 블럭의 첫 번째 부분은 UIAlert Action의 handler 파라미터들과 리턴 타입를 정의하는 것이고, in 키워드는 이후로는 closure의 몸체 부분이 시작되는 부분이다.

NewImage

위 처럼 closure를 각 변수나 상수로 정의하는 것은 필수가 아니다. 아래와 같이 간단하게 closure을 파라미터처럼 활용 할 수도 있다. 아래와 같이 간단하게 사용 할 수 있다.

let callAction = UIAlertAction(title: "Call", style: .Default, handler: {
            action in
                let alertMessage = UIAlertController(title: "Service Unavailable", message: "Sorry, the call feature is not available yet. Please retry later.", preferredStyle: .Alert)
                alertMessage.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
                self.presentViewController(alertMessage, animated: true, completion: nil)
                }
        )
alertController.addAction(callAction)

결론

UIAlertController는 clsures의 사용 (또는 Objective-C의 블럭)을 포용하고 있으며, 이로써 우리는 UIAlertController을 잘 활용 할 수 있을 것이라고 예상한다.

Clojure – Clojure Cider REPL에서 쓰래드를 사용하였을 때 print 되지 않는 이유와 해결 방법

참고 링크

Q> Emacs Cider REPL에서 백그라운드 쓰레드에서 println을 돌렸을 때 왜 출력이 되지 않는가?

A> 원인은 출력되는 버퍼가 다르기 때문이다.

println의 행동은 동적으로 &#42;out&#42; 라는 output stream에 바인딩 되어 출력되는데, 만약 thread를 사용한다면, 그 thread는 루트 &#42;out&#42; 에 바인딩 될것이고, 그렇게 되면 사용하고 있는 REPL에서는 확인을 할 수 없다.

그러면 어디서 확인할 수 있는가?

buffer list에서 *nrepl-server*라는 이름을 가진 버퍼에서 확인 할 수 있다.

또는

(alter-var-root #'*out* (constantly *out*))

와 같이 바꾸어 기존 buffer에서 확인 할 수 있다.

참고 링크

만약 Cider REPL을 사용하지 않는다면, nrepl을 시작한 터미널에서 확인 할 수 있다.