먼 옛날 은하계에서 멀리 떨어져있는 소프트웨어 개발자들은 TCP / IP 소켓으로 클라이언트 - 서버 애플리케이션을 작성했었습니다. 그것은 어둠의 시대 이전, HTTP 이전이었지요.
물론 농담입니다. HTTP는 다양한 클라이언트 - 서버 응용 프로그램을 제공하기 위해 활용 될 수 있으며 REST 응용 프로그램의 기반에 있습니다. HTTP 가 테이블에 가져오는것은 와이어상에서 패킷을 직접가져오는 작업은 아니지만 합의 된 프로토콜 구조 (그리고 어느 포트가 사용되는지에 대한 표준에 어느 정도의 차이가 있음) 는 패킷에 대한 것입니다. GET, POST, PUT 등과 같은 동작 동사와 HTTP 헤더 자체가 클라이언트 - 서버 응용 프로그램을 개발하는 데 HTTP 를 이상적으로 만들어 줍니다.
결국 스택 맨 아래에서 운영 체제의 소켓 인터페이스를 통해 비트와 바이트가 마샬링됩니다. 네트워크 소켓과 상호 작용하기 위한 API는 매우 풍부하며 이 주제에 대한 많은 자습서와 서적이 있습니다. C의 IP 네트워킹 루틴은 상당히 장황 할 수 있으며 C ++로 객체 지향 루틴에 캡슐화 된 최초의 "실제"API 중 하나였습니다. 그 전통은 Foundation의 CFStream 클래스와 이제는 Swift swiftysockets API로 이어졌습니다.
Swiftychat
Swift에서 TCP / IP 네트워크 소켓을 사용하는 방법을 설명하기 위해 기본 "채팅 시스템" 응용 프로그램 인 Swiftychat 을 개발할 것입니다. 기능면에서 제한적이며 실제로 사용하기에 별다른 의미는 없지만 Swift의 TCP / IP 소켓에서 문자열을 보내고받는 방법의 실제 예입니다.
swiftysockets
Swiftychat는 원래 Zewo 팀이 개발 한 Swift Package Manager-ready TCP / IP 소켓 구현 인 swiftysockets를 사용합니다. 불행히도 패키징 제약으로 인해 우리 시스템에 기본 C 라이브러리 Tide를 설치하기 위해 먼저 약간의 난리법석을 떨어야 합니다. 그럼 이제 해보죠.
IP와 TCPServerSocket 클래스 (swiftysockets)가 모두 오류를 던질 수 있으므로 nil이 가능한 반환 값임을 알리는 이니셜 라이저. IP는 IP 주소와 포트 정보를 멋지게 캡슐화하고 TCPServerSocket 초기화 프로그램에 인스턴스를 제공합니다. initsucceeds가되면 우리는 주어진 포트에 들어오는 연결을 받아 들일 준비가 된 TCP 소켓을 갖게됩니다.
2. The Main Loop
함수의 이름을 startListening, start, main으로 지정하십시오. 새 클라이언트 연결 (server! .accept ())을 받아들이고 연결된 클라이언트 목록에 추가하는 기본 이벤트 루프입니다. server! .accept ()는 새로운 연결을 기다리고 받아드리는 기능입니다. 꽤 표준적인 것들이죠.
3. Client Management
나머지 ChatterServer에는 모든 "클라이언트 관리"기능이 포함되어 있습니다. 클라이언트를 관리하는 몇 가지 변수와 세 가지 루틴이 있습니다.
우리의 변수는 간단합니다.
연결되어진 클라이언트의 배열 ([TCPClientSocket])
클라이언트를 구분하는데 사용되는 연결 카운트
함수내용은 이렇습니다:
addClient는 TCPClientSocket을 사용하여 연결 수를 증가시키고 클라이언트의 연결에 따라 "관리"되는 NSThread를 설정합니다. 추가 연결이 새로 추가됨에 따라 NSThread가 생성됩니다. 잠시 후에 NSThreadroutine에 대해 이야기하겠습니다. 스레드가 시작되면 addClient는 연결된 클라이언트 배열 끝에 TCPClientSocket을 추가합니다.
removeClient는 지정된 클라이언트를 "필터 아웃"하기 위해 필터 함수를 사용하여 연결된 클라이언트 목록에서 클라이언트를 제거합니다. 여기에! == identity 연산자를 사용합니다.
broadcastMessage는 ChatterServer를 채팅 서버로 만드는 것입니다. where 키워드를 사용하여 클라이언트가 만든 메시지를 다른 모든 연결된 클라이언트에 브로드 캐스팅하는 필터링 된 배열을 만듭니다. 다시, 우리는! == 연산자를 사용합니다.
스레드는 주 프로세스 내에서 실행되는 별도의 실행 경로임을 상기하십시오. 우리 서버는 연결된 각 클라이언트에 대해 별도의 스레드를 생성합니다. 이제는 그것이 좋은 아이디어인지 아닌지에 대해서는 상황에 따라 달라집니다만 수만 명의 클라이언트를 처리 할 서버를 설계한다면, 말리고 싶습니다.
클라이언트 처리 스레드는 또한 TCPClientSocket 클래스의 receiveString 메서드를 통해 입력을 기다리는 루프에 위치합니다. 문자열이 수신되면 서버는 이를 콘솔에 기록한 다음 응답을 브로드 캐스트합니다. try 시도로 인해 연결이 끊어지면 서버는 클라이언트를 제거합니다.
Putting it All Together
우리의 목표는 가능한 한 많이 Swift Package Manager를 사용하여 응용 프로그램을 빌드하는 것입니다. swiftpm에 대한 소개는 자습서를 확인하십시오.
Sources라는 디렉토리에 main.swift 및 ChatterServer.swift 코드를 추가하십시오.
swifty빌드를 실행하면 두 가지 종속성 (Tide 및 swiftysockets)을 다운로드하여 빌드하고 응용 프로그램 코드를 컴파일해야합니다. 모두 잘되면 .build / debug / 디렉토리에 chatterserver라는 이름의 바이너리가 생깁니다.
Testing it Out
다음 튜토리얼은 멋진 chat 클라이언트를 작성하는 것이지만 지금은 nc (netcat) 명령으로 서버를 테스트 할 수 있습니다. 서버를 시작한 다음 다른 터미널 창에서 nc localhost 5555를 입력하십시오. 연결된 서버 1의 클라이언트 1 창이 표시됩니다. netcat "클라이언트"창에서 CTRL-C를 누르면 서버는 그 이유와 함께 연결 해제 메시지를 인쇄합니다 (예 : 연결 재설정 (연결 재설정)).
실제 테스트를 위해 서버와 연결된 세 개의 클라이언트를 시작합니다.
Chatville
왼쪽 터미널에서 채팅 서버가 실행 중입니다. 오른쪽에는 3 개의 클라이언트가 있으며, 각각은 nc localhost 5555 명령을 입력하여 시작됩니다. 각 클라이언트가 연결되면 서버는 연결 메시지를 인쇄합니다.
우리의 broadcastMessage 함수는 브로드 캐스트로부터 메시지의 송신자를 제외시킵니다. 이렇게하면 클라이언트가 자신의 메시지를 다시받지 못하게 되겠지요.
What’s Next
nc를 클라이언트로 사용하는 것은 약간 지루합니다. 닉네임을 설정할 수없고, 메시지에 "구조"가없고, 타임 스탬프가 없습니다. 위의 예에서 메시지를받는 사람은 누가 작성했는지 전혀 알지 못합니다! swiftysockets에는 이미 TCPClientSocket 클래스가 있으므로 더 강력한 채팅 클라이언트를 만들수 있습니다.
Getting the Code
here에 우리의 작은 채팅 서버에 대한 코드를 넣었습니다. 또한 현재 구현되지 않은 채터 클라이언트 프로젝트도 포함되어 있습니다. 다운로드를 하고 최상위 디렉토리에 make를 입력하면 클라이언트와 서버가 모두 빌드됩니다. 주의 사항 : swiftysockets을 사용하기 전에 libtide.a와 관련 헤더를 설치해야합니다!
소켓은 양방향으로 데이터를 전송할 수있는 도구입니다. 따라서 소켓은 양면을 가지며, 각면은 IP 주소와 포트라는 두 요소의 조합으로 식별됩니다.
데이터가 전송되는 방식 (프로토콜)이 다른 많은 종류의 소켓이 있습니다. 가장 많이 사용되는 유형은 TCP와 UDP 이며 . 이 튜토리얼에서는 TCP 소켓을 다루겠습니다.
iOS 프로젝트를 작성하기 전에 Python 언어를 사용하여 TCP 서버를 만들 계획입니다. 그러나 특정 서버 구현이 iOS 기술과 관련이 없으므로 여기서 건너 뛰고 서버가 준비 되었다고 가정합니다. 이제App Client에 초점을 맞추고소켓 프로그래밍을 해 보겠습니다. 클라이언트는 채팅 룸 가입, 메시지 보내기 및 메시지 수신이라는 세 가지 주요 작업을 합니다.
Stream Programming
iOS에서 소켓 연결을 설정하기 위해 우리는 스트림을 사용합니다. 스트림은 데이터를 송수신하는 메커니즘에 대한 추상화입니다. 또한 스트림에는 "연결이 열려 있습니다", "데이터가 수신되었습니다", "연결이 닫혔습니다"등과 같은 특정 이벤트에 따라 대응할 수있는 대리자가 연결되어 있습니다.
Cocoa Framework에 포함 된 스트림과 관련된 중요한 클래스가 있습니다.
NSStream This is the super class which defines some abstract features like open, close and delegate.
NSInputStream A subclass of NSStream for reading input.
NSOutputStream A subclass of NSSTream for writing output.
여기있는 유일한 문제는 NSStream 클래스가 원격 호스트에 연결할 수없는 반면 CFStream은 능력이 있다는 것입니다. 다행스럽게도 NSStream과 CFStream은 일종의 브리지 연결이므로 NSStream 형식의 CFStream을 쉽게 가져올 수 있습니다.
CFStreamCreatePairWithSocketToHost는 두 스트림을 호스트와 포트에 바인딩하는 데 사용됩니다. 일단 호출하면 CFStream을 NSStream으로 자유롭게 캐스팅 할 수 있습니다.
스트림은 지속적으로 데이터를 보내거나받을 준비가되어 있어야합니다. 이 기능을 사용하려면 실행 루프에서 이벤트를 수신하도록 스트림을 예약해야합니다. 앱은 이벤트를 스트리밍하는 데 반응해야하지만 이벤트의 자비가되지는 않습니다. 실행 루프 스케줄링을 사용하면 다른 코드 (필요한 경우)를 실행할 수 있지만 스트림에서 문제가 발생하면 알림을받을 수 있습니다.
Joining the Chat
서버에 연결되면 채팅에 참여할 준비가되었습니다. 조인 메시지의 형식은 "iam : name"입니다. 그래서 우리는 문자열을 만들어서 outputStream에 써야합니다.
이벤트를 전달하기 위해 서버는 클라이언트 / 서버가주고받는 데이터의 예상 포맷 / 시퀀스를 정의하는 프로토콜을 정의해야합니다. 이 응용 프로그램에서는 매우 간단한 문자열 기반 프로토콜을 사용합니다. iammeans "사용자가 채팅에 참여했습니다."msg는 "메시지 보내기"를 나타냅니다.
Sending Messages
채팅에 참여하는 것과 매우 유사한 방식으로이를 구현합니다. "iam :"을 "msg :"로 전환하면됩니다.
while 루프를 사용하여 스트림의 바이트를 수집합니다. 스트림에 아무것도 남아 있지 않으면 read 메소드는 0을 리턴합니다. 결과가 0보다 큰 경우 버퍼를 String으로 변환하고 결과를 출력합니다.
기기에서 앱 실행 :
먼저 "localhost"문자열을 컴퓨터의 ip로 전환하십시오. 현재 IP를 찾으려면 "시스템 환경 설정> 네트워크"로 가십시오. 장치는 무선으로 컴퓨터를 서비스하는 동일한 라우터에 연결해야합니다. 3G 연결을 사용하려면 포트 80에서 네트워크 외부의 연결을 허용하도록 라우터를 구성해야합니다 (권장하지 않음).
스위프트의 인기가 높아지면서 새 프로젝트를 시작할때 스위프트를 사용할 기회 또한 많아지고 있다. 시간도 절약하고 좀 더 쉽게 스위프트로 iOS 개발을 하기위해 여기 10가지 라이브러리를 소개하고자 한다.
이전에 언급한 GitHub 와 Bitbucket 는 훌륭한 iOS 라이브러리를 찾기 위한 좋은 장소이다. CocoaPods 나 Carthage 같은 툴은 라이브러리를 신속하게 인스톨링하고 관리하는데 도움을 줄 수 있다. 이 방식은 프로젝트 디펜던시를 관리하는것을 쉽게 만들어 줄 것이다.
앱에 네트워킹을 위한 기능을 간단히 추가하고 싶을때, Alamfire 는 안성마춤이다.Alamofire 는 HTTP 네트워킹 라이브러리이고 NSURLSession 과 Foundation URL 로딩 시스템에 기반해서 만들어졌다. 우아하고 간단한 스위프트 인터페이스로 네트워킹 메카니즘을 깔끔하게 감쌌다.
// Making a GET request
Alamofire.request(.GET,"https://httpbin.org/get",parameters:["foo":"bar"]).responseJSON{responseinprint(response.request)// original URL request
print(response.response)// URL response
print(response.data)// server data
print(response.result)// result of response serialization
ifletJSON=response.result.value{print("JSON: \(JSON)")}}
Swift의 명시적 유형은 코드로 인한 실수를 저지르지 않게 도와주어 버그가 발생하지 않도록 합니다. 그러나 때로는 JSON을 사용하여 작업 할 때 특히 문제가 될 수 있습니다. 다행스럽게도 SwiftyJSON은 Swift의 JSON 데이터를 보다 읽기 쉬운 방식으로 처리하는 데 도움이됩니다. 선택적 언 래핑은 자동으로 처리됩니다!
// Typical JSON handling
ifletstatusesArray=try?NSJSONSerialization.JSONObjectWithData(data,options:.AllowFragments)as?[[String:AnyObject]],letuser=statusesArray[0]["user"]as?[String:AnyObject],letusername=user["name"]as?String{// Finally we got the username
}// With SwiftyJSON
letjson=JSON(data:dataFromNetworking)ifletuserName=json[0]["user"]["name"].string{//Now you got your value
}
API를 통해 정보를 다운로드하는 앱을 작성한 적이 있다면 아마도 응답을 객체에 매핑하는 코드를 작성하는 데 많은 시간을 할애했을 것입니다. ObjectMapper를 사용하면 JSON 응답을 모델 객체로 변환하거나 그 반대로 변환 할 수 있습니다. 즉, JSON을 객체에 매핑하고 객체를 JSON에 매핑하는 데 도움이됩니다. 중첩 된 객체도 지원됩니다.
// Temperature class that conforms to Mappable protocol
structTemperature:Mappable{varcelsius:Double?varfahrenheit:Double?init?(_map:Map){}mutatingfuncmapping(map:Map){celsius<-map["celsius"]fahrenheit<-map["fahrenheit"]}}
또한 ObjectMapper를 사용할 때 JSON 응답 데이터를 Swift 객체로 변환하는 AlamofireObjectMapper라는 Alamofire 확장 기능에 대해서도 언급 할 가치가 있습니다.
Quick는 RSpec, Specta 및 Ginkgo에서 영감을 얻은 Swift의 행동 중심 개발 프레임 워크입니다. Quick는 Nimble과 함께 제공됩니다. Nimble은 테스트를 위한 matcher 프레임 워크입니다.
// Documentation directory spec
classTableOfContentsSpec:QuickSpec{overridefuncspec(){describe("the 'Documentation' directory"){it("has everything you need to get started"){letsections=Directory("Documentation").sectionsexpect(sections).to(contain("Organized Tests with Quick Examples and Example Groups"))expect(sections).to(contain("Installing Quick"))}context("if it doesn't have what you're looking for"){it("needs to be updated"){letyou=You(awesome:true)expect{you.submittedAnIssue}.toEventually(beTruthy())}}}}}
Eureka는 간단하고 우아한 방식으로 동적 테이블 뷰 형식을 작성할 수 있도록 도와줍니다. 그것은 행, 섹션 및 양식으로 구성됩니다. 앱에 많은 양식이 포함되어있는 경우, 유레카 (Eureka)는 실시간 보호 기능을 입증합니다.
// Creating a form
classCustomCellsController:FormViewController{overridefuncviewDidLoad(){super.viewDidLoad()form+++Section("Custom cells")<<<WeekDayRow(){$0.value=[.Monday,.Wednesday,.Friday]}<<<TextFloatLabelRow(){$0.title="Float Label Row, type something to see.."}}}
RxSwift는 Functional Reactive Programming을위한 신속한 프레임 워크입니다. 보다 구체적으로, RxSwift는 Rx의 신속한 버전이며 목표는 비동기 작업과 이벤트 / 데이터 스트림을 쉽게 구성 할 수있게하는 것입니다. KVO 관찰, 비동기 작업 및 델리게이트는 모두 시퀀스 추상화로 통일되어 RxSwift를 강력한 프레임 워크로 만듭니다. ReactiveCocoa를 사용해 본 경험이 있다면 그 개념을 잘 알고있을 것입니다.
// Combine first and last name sequences, and bind the resulting string to label
combineLatest(firstName.rx_text,lastName.rx_text){$0+" "+$1}.map{"Greeting \($0)"}.bindTo(greetingLabel.rx_text)
SnapKit은 가독성을 잃지 않으면 서 최소한의 코드만으로 자동 레이아웃을 코드에 작성하는 것을 단순화하는 자동 레이아웃 라이브러리입니다. 사용자 인터페이스를 코딩하는 동안 프로그래밍 오류를 피할 수 있도록 설계된 유형 안전타입입니다.
// Resizing and centering subview in its superview
letbox=UIView()letcontainer=UIView()container.addSubview(box)box.snp_makeConstraints{(make)->Voidinmake.size.equalTo(50)make.center.equalTo(container)}
Spring은 코드에서 또는 스토리 보드에서 직접 애니메이션을 생성하는 데 도움이되는 애니메이션 라이브러리입니다. 런타임 속성을 사용하여 스토리 보드에서 애니메이션을 만들 수 있습니다 (IBInspectable 속성을 통해 설정). Spring은 많은 이미 작성된 애니메이션, 전환 및 속성을 지원하는 완전히 개발 된 애니메이션 라이브러리로 성장했습니다.
// Usage with code
layer.animation="wobble"layer.animate()
CoreStore는 Core Data의 래퍼 라이브러리입니다. 그 목표는 핵심 데이터와 상호 작용할 때 유형 안전성과 Swift의 우아함을 제공하는 것입니다. CoreStore의 API는 데이터베이스와 효과적으로 상호 작용하는 모든 일반적인 방법을 제공합니다.
// Storing a person entity
CoreStore.beginAsynchronous{(transaction)->Voidinletperson=transaction.create(Into(MyPersonEntity))person.name="John Smith"person.age=42transaction.commit{(result)->Voidinswitchresult{case.Success(lethasChanges):print("success!")case.Failure(leterror):print(error)}}}