사용자 정의 Observable
흐름을 만듭니다.
neverCompleteObservable
는 onNext
를 호출하고 프린트합니다. onCompleted
가 호출되지 않습니다 ㅋㅋ
func testCreate() {
let disposeBag = DisposeBag()
let neverCompleteObservable = { (element: String) -> Observable<String> in
return Observable.create { observer in
observer.on(.next(element))
print("observer!!!!")
// observer.on(.completed)
return Disposables.create()
}
}
neverCompleteObservable("🔴")
.subscribe(onNext: {print($0)}, onError: nil, onCompleted: {print("complete!")}, onDisposed: {print("disposed!")})
.disposed(by: disposeBag)
}
🔴
observer!!!!
disposed!
scheduler: ImmediateSchedulerType
조건에 맞을 때까지 계속 item을 발생시킵니다. (while 구문같네요)
뭐, 1부터 100까지 짝수만 출력하고 싶다면?
func testGenerate() {
let disposeBag = DisposeBag()
Observable.generate(
initialState: 2,
condition: { $0 <= 100 },
iterate: { $0 + 2 }
)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
2
4
...
98
100
관찰자가 구독을 시작하기 전까지 Observable
을 생성하지 않습니다. Cold 그리고 관찰자가 구독을 시작할 때 매번 새로운 Observable
을 만들어 냅니다!
func testDeferred() {
let disposeBag = DisposeBag()
var count = 1
let deferredSequence = Observable<String>.deferred {
print("Creating \(count)")
count += 1
return Observable.create { observer in
print("Emitting...")
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐵")
return Disposables.create()
}
}
deferredSequence
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
deferredSequence
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
구독 후 dispose를 했지만, 다시 subscribe를 시작하면 다시 observable을 만들어 냅니다!
func testNeverEmptyError() {
let disposeBag = DisposeBag()
let neverSequence = Observable<String>.never()
let neverSequenceSubscription = neverSequence
.subscribe { _ in
print("This will never be printed")
}
neverSequenceSubscription.disposed(by: disposeBag)
Observable<Int>.empty()
.subscribe { event in
print(event)
}
.disposed(by: disposeBag)
Observable<Int>.error(TestError.test)
.subscribe { event in
print(event)
}
.disposed(by: disposeBag)
}
// neverSequenceSubscription의 onNext는 불리지 않는다.
completed // empty는 onCompelete Event만 발생
error(test) // error는 onError Event만 발생
func testFromJust() {
let disposeBag = DisposeBag()
let array = ["🐶", "🐱", "🐭", "🐹"]
print("from")
Observable.from(array)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
print("just")
Observable.just(array)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
from
🐶
🐱
🐭
🐹
just
["🐶", "🐱", "🐭", "🐹"]
여러 항목을 파라미터로 받아서 각 항목에 1개씩 배출하는 Observable Sequence를 만듭니다.
func testOf() {
let disposeBag = DisposeBag()
Observable.of("🐶", "🐱", "🐭", "🐹")
.subscribe(onNext: { element in
print(element)
})
.disposed(by: disposeBag)
}
🐶
🐱
🐭
🐹
from은 1개의 파라미터를 받아서 각 요소별로 항목을 만들고, of는 여러개의 파라미터를 받아서 항목을 만들며, Just는 1개의 파라미터를 받아서 1개의 항목을 만듭니다!
1씩 증가하는 Int
값을 주어진 개수만큼 배출하는 Observable Sequence를 만듭니다.
func testRange() {
let disposeBag = DisposeBag()
Observable.range(start: 1, count: 10)
.subscribe { print($0) }
.disposed(by: disposeBag)
}
next(1)
next(2)
...
next(9)
next(10)
항목을 무한대로 뽑아내는 Observable Sequence를 만듭니다.
무한으로 뽑아내니까 take를 함께 써보았습니다! take(count:)
는 Observable Sequence에서 특정 개수만큼만 수신하고 구독을 해제합니다.
take의 자매품으로
take(duration:, scheduler:)
takeLast(count:)
,takeUntil(other:)
,takeUntil(behavior:, predicate)',
takeWhile(predicate:)` 가 있습니다.
func testRepeat() {
let disposeBag = DisposeBag()
let observable = Observable.repeatElement("🐶")
observable
.take(3)
.subscribe(onNext: { print("1 : \($0)")})
.disposed(by: disposeBag)
}
1 : 🐶
1 : 🐶
1 : 🐶
2 : 🐶
func testInterval() {
let interval = Observable<Int>.interval(.milliseconds(500), scheduler: SerialDispatchQueueScheduler.init(qos: .background))
.subscribe(onNext: { item in
print(item)
}, onCompleted: {
print("completed")
}, onDisposed: {
print("disposed")
})
Thread.sleep(forTimeInterval: 4.0)
interval.dispose()
}
0
1
2
3
4
5
6
7
disposed
타이머를 실행하기 위해 지정된 스케줄러를 사용하여 지정된 초기 상대 시간 경과 후에 값을 주기적으로 생성하는 관찰 가능 시퀀스를 반환합니다.
dueTime: RxTimeInterval
: 첫 번째 값을 내보낼때까지 기다릴 시간period: RxTimeInterval
: 후속 값을 산출하는 시간scheduler: SchedulerType
: Timer를 생성할 Scheduler func testTimer() {
let timer = Observable<Int>.timer(.seconds(1), period: .seconds(1), scheduler: SerialDispatchQueueScheduler.init(qos: .background))
.subscribe(onNext: { item in
print(item)
}, onCompleted: {
print("completed")
}, onDisposed: {
print("disposed")
})
Thread.sleep(forTimeInterval: 4.0)
timer.dispose()
}
0
1
2
3
disposed
Observer 패턴을 확장해서 데이터 / 이벤트 에 대한 시퀀스를 지원하고, 저레벨 쓰레딩, 동기화, 스레드 안정성, 동시성 데이터 구조, 그리고 I/O를 막지 않는 것들에 대한 추상화된 개념과 함께 선언적으로 사용할 수 있는 연산자들을 추가했습니다.
ReactiveX에서 관찰자는 Observable
을 구독합니다. 관찰자는 Observable
이 배출해내는 item이나 item 흐름에 반응합니다. 이 패턴을 사용하면 Observable이 item을 배출해내기까지 어떤 흐름을 차단할 필요가 없으며, item을 배출해낼 때까지 기다립니다.
결국 Observable 이란, 관찰자가 반응할 만한 item/혹은 새로운 흐름을 배출해낼 수 있는 것을 의미합니다.
Rx를 쓰지 않고도 기존 DispatchQueue
를 사용해서도 비동기 작업을 쓸 수 있습니다. 하지만 Rx를 쓰면 비동기 작업을 좀 더 간결하게 작성할 수 있으며, Rx가 제공하는 여러 연산자를 이용해서 간편하게 비동기 작업을 할 수 있습니다.
많은 소프트웨어 프로그래밍 작업에서 작성한 지침은 작성한 순서대로 점진적으로 실행되고 완료됩니다. 그러나 ReactiveX에서 많은 명령어가 병렬로 실행될 수 있으며 나중에 결과가 “관찰자”에 의해 임의의 순서로 캡처됩니다. 메소드를 호출하는 대신 데이터를 검색하고 변환하는 메커니즘을 Observable
관찰자를 구독하고 그 시점에서 이전에 정의 된 메커니즘이 관찰자가 보초를 세우고 준비가 될 때마다 배출량을 포착하고 대응할 수 있도록 조치를 취합니다.
이 방법의 장점은 서로 의존하지 않는 여러 작업을 할 때 다음 작업을 시작하기 전에 각 작업이 완료되기를 기다리는 대신 동시에 작업을 시작할 수 있다는 것입니다. 작업 묶음은 번들 중 가장 긴 작업으로 완료하는 데 오랜 시간이 걸립니다.
비동기 프로그래밍 및 디자인의이 모델을 설명하는 데 사용되는 용어가 많이 있습니다. 이 문서는 다음 용어를 사용합니다 : 관찰자가 Observable을 구독합니다. Observable은 관찰자의 method를 호출하여 관찰자에게 항목을 내보내거나 통지를 보냅니다.
Subscribe 메소드는 어떻게 Observable에 관찰자를 연결할 것인가?에 대한 것입니다. 관찰자는 기본적으로 다음 메소드들을 가집니다.
onNext
Observable이 새로운 항목을 배출할 때마다 호출됩니다. 이 메서드는 호출된 아이템을 parameter로 받습니다.
Observable이 예상했던 데이터를 생성하지 못했거나, 혹은 다른 오류가 발생했을 때 이 메서드를 호출합니다. 이 메서드가 호출되면 onNext
나 onCompleted
가 더 이상 호출되지 않습니다. onError
메서드는 오류를 나타내는 객체를 parameter로 받습니다.
onCompleted
onNext
가 호출된 이후에, 어떠한 오류도 발생되지 않았다면 마-지막에 이 메서드가 호출됩니다.
onNext
는 매번 불리지만,onError
와onCompleted
는 item이 배출되고 마지막에 불립니다. (둘 다 부를 수는 없습니다.) 그래서onNext
는 item을 ‘배출’한다고 하고,onError
와onCompleted
는 item을 ‘소비’한다고들 합니다.
일부 ReactiveX 구현에는 unsubscribe
메서드를 구현하는 인터페이스인 Subscriber
라는 관찰자가 있습니다. 이 메서드를 호출하여 구독자가 현재 구독중인 Observable
에 더 이상 관심이 없다는 것을 나타낼 수 있습니다. 이 Observable
을 구독하는 다른 관찰자가 없다면 새로운 항목을 배출하는 것을 그만두도록 선택할 수 있습니다.
이 구독 취소의 결과는 관찰자가 구독 한 Observable에 적용되는 연산자 체인을 통해 다시 연결되어 체인의 각 링크가 항목을 내보내는 것을 중지시킵니다. 이것은 즉각적으로 수행되는 것을 보장하지 않기때문에, Observable은 더 이상 관찰자가 없음에도 잠시 동안 항목을 생성하고 방출하려고 시도 할 수 있습니다.
ReactiveX는 각 언어별로 고유한 이름을 지정하고 있습니다. 정식으로 지정된 naming 표준은 없지만, 구현상 많은 공통점이 있습니다.
또한 이 이름 중 일부는 다른 문맥에서 다른 의미를 가지거나 특정 구현 언어의 관용구에 어색한 것으로 보입니다.
예를 들어 onEvent
이름 패턴을 봅시다. (예 : onNext, onCompleted, onError). 어떤 상황에서는 이러한 이름이 event handler를 등록하는 방법처럼 보이지만, ReactiveX에서는 이벤트 핸들러 자체의 이름을 지정합니다.
Observable이 언제 항목을 발생하는지에 따라 Hot/Cold로 나눌 수 있습니다.
ReactiveX에는 “Connectable” Observable이 있습니다. 이것은 관찰자의 구독여부와 관계 없이 Connect 메서드가 호출될 때까지 항목 방출을 시작하지 않습니다.
Observables와 Observer는 ReactiveX의 시작일뿐입니다. 그 자체만으로도 표준 관찰자 패턴을 조금 확장한 것일 뿐이며 단일 콜백보다는 일련의 이벤트를 처리하는 데 더 적합합니다.
진정한 힘은 Observables가 방출하는 일련의 항목을 변형, 결합, 조작 및 작업 할 수 있게 해주는 연산자인 “Reactive Extensions”(ReactiveX)에서 나옵니다.
이러한 Rx 연산자를 사용하면 비동기 시퀀스를 선언적 방식으로 함께 구성하여 콜백의 모든 효율성 이점을 얻을 수 있지만 일반적인 비동기 시스템과 관련된 중첩 콜백 처리기의 단점은 없습니다.
대부분의 연산자는 Observable에서 작동하고 Observable을 반환합니다. 이렇게 하면 체인에서 이러한 연산자를 차례로 적용 할 수 있습니다. 체인의 각 연산자는 이전 연산자의 연산결과 인 Observable을 수정합니다.
Builder 패턴와 같이, 이 패턴에서는 특정 클래스의 연산 메소드를 통해 동일한 클래스 항목에 대한 수정을 할 수 있습니다. 이러한 패턴을 사용하면 비슷한 방식으로 메서드를 연결할 수 있습니다. 그러나 빌더 패턴에서 메소드가 체인에 나타나는 순서는 일반적으로 중요하지 않습니다. Observable 연산자는 중요 사항을 순서대로 지정합니다.
Observable 연산자 체인은 체인을 생성 한 원래 Observable에서 독립적으로 작동하지 않으며, 체인 되기 직전의 연산자가 생성한 Observable에서 각각 차례로 작동합니다.
2019.07.01에 작성되었습니다.
요새 하이퍼커넥트(우왕) 에서 인턴쉽을 하고 있습니다!!!!!!!! 영광영광 대영광!!
1주차 인턴쉽의 목표는 Flickr API를 사용해서 이미지 피드 만들기와 RxSwift를 공부하기 입니다.
RxSwift를 공부하되 RxSwift 없이 프로젝트를 개발해보는 것이 목표라서 RxSwift를 공부하기 앞서 Flickr API를 살펴보았습니다.
사용할 API는 다음곽 같습니다.
이를 postman 에서 호출하면 다음과 같은 결과가 나옵니다!
```xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:flickr="urn:flickr:user"
xmlns:media="http://search.yahoo.com/mrss/">
<title>Uploads from everyone</title>
<link rel="self" href="http://www.flickr.com/services/feeds/photos_public.gne" />
<link rel="alternate" type="text/html" href="https://www.flickr.com/photos/"/>
<id>tag:flickr.com,2005:/photos/public</id>
<icon>https://combo.staticflickr.com/pw/images/buddyicon.gif</icon>
<subtitle></subtitle>
<updated>2019-07-01T06:16:46Z</updated>
<generator uri="https://www.flickr.com/">Flickr</generator>
<entry>
<title>DAXN_18-07-07_Canon EOS 7D_IMG_0899.jpg</title>
<link rel="alternate" type="text/html" href="https://www.flickr.com/photos/xeiss/48166952616/"/>
<id>tag:flickr.com,2005:/photo/48166952616</id>
<published>2019-07-01T06:16:46Z</published>
<updated>2019-07-01T06:16:46Z</updated>
<flickr:date_taken>2018-07-07T17:20:39-08:00</flickr:date_taken>
<dc:date.Taken>2018-07-07T17:20:39-08:00</dc:date.Taken>
<content type="html"> <p><a href="https://www.flickr.com/people/xeiss/">xeiss</a> posted a photo:</p>
<p><a href="https://www.flickr.com/photos/xeiss/48166952616/" title="DAXN_18-07-07_Canon EOS 7D_IMG_0899.jpg"><img src="https://live.staticflickr.com/65535/48166952616_489f047e57_m.jpg" width="240" height="160" alt="DAXN_18-07-07_Canon EOS 7D_IMG_0899.jpg" /></a></p>
</content>
<author>
<name>xeiss</name>
<uri>https://www.flickr.com/people/xeiss/</uri>
<flickr:nsid>26977653@N00</flickr:nsid>
<flickr:buddyicon>https://farm6.staticflickr.com/5463/buddyicons/26977653@N00.jpg?1375250332#26977653@N00</flickr:buddyicon>
</author>
<link rel="enclosure" type="image/jpeg" href="https://live.staticflickr.com/65535/48166952616_489f047e57_b.jpg" />
<category term="2018" scheme="https://www.flickr.com/photos/tags/" />
<category term="7d" scheme="https://www.flickr.com/photos/tags/" />
<category term="holiday" scheme="https://www.flickr.com/photos/tags/" />
<category term="mission" scheme="https://www.flickr.com/photos/tags/" />
<category term="pas" scheme="https://www.flickr.com/photos/tags/" />
<category term="pasikuda" scheme="https://www.flickr.com/photos/tags/" />
<category term="srilanka" scheme="https://www.flickr.com/photos/tags/" />
<category term="trip" scheme="https://www.flickr.com/photos/tags/" />
<category term="vacation" scheme="https://www.flickr.com/photos/tags/" />
<displaycategories>
</displaycategories>
</entry>
...
</feed>
우왕 XML이다…
XML을 파싱하는 것 말고 JSON으로 받아오는 방법이 있을거라고 생각해서 API 문서를 탐독하기 시작합니다.
Flickr - Feed Format문서에 따르면 rss2, rss1, Atom 등을 비롯해서 php, php_serial, csv, json, sql, yaml, cdf등 다양한 format으로 요청할 수 있다고 합니다.
그리고 원하는 format을 요청하려면 format 매개 변수를 사용하라고 하네요. 그래서 postman에 query parameter를 추가해줬습니다.
https://www.flickr.com/services/feeds/photos_public.gne?format=json
그 결과는?
jsonFlickrFeed({
"title": "Uploads from everyone",
"link": "https:\/\/www.flickr.com\/photos\/",
"description": "",
"modified": "2019-07-01T15:00:13Z",
"generator": "https:\/\/www.flickr.com",
"items": [
{
"title": "Semana de la Dulzura",
"link": "https:\/\/www.flickr.com\/photos\/144395899@N06\/48170431461\/",
"media": {"m":"https:\/\/live.staticflickr.com\/65535\/48170431461_6d238cef55_m.jpg"},
"date_taken": "2019-07-01T11:20:12-08:00",
"description": " <p><a href=\"https:\/\/www.flickr.com\/people\/144395899@N06\/\">claudiazwenguer<\/a> posted a photo:<\/p> <p><a href=\"https:\/\/www.flickr.com\/photos\/144395899@N06\/48170431461\/\" title=\"Semana de la Dulzura\"><img src=\"https:\/\/live.staticflickr.com\/65535\/48170431461_6d238cef55_m.jpg\" width=\"216\" height=\"240\" alt=\"Semana de la Dulzura\" \/><\/a><\/p> ",
"published": "2019-07-01T15:00:13Z",
"author": "nobody@flickr.com (\"claudiazwenguer\")",
"author_id": "144395899@N06",
"tags": ""
}
]}
)
뭔가 깔끔해진게, JSON인 것 같은데 jsonFlickrFeed라는 이름으로 감싸져 있습니다. 우리가 알던 JSON의 형태가 아니네요.
그래서 쟤를 어떻게 하면 벗기고 안의 JSON을 가져올 수 있을까, 한참을 찾아보면서 저 형식이 JSONP라는 것을 알게됐습니다.
그래서 JSONP가 뭔가요? 도와줘요 위키피디아!
JSONP(JSON with Padding 또는 JSON-P[1])는 클라이언트가 아닌, 각기 다른 도메인에 상주하는 서버로부터 데이터를 요청하기 위해 사용된다. 2005년에 Bob Ippolito가 제안하였다.[2] JSONP는 동일-출처 정책을 우회하는 데이터의 공유를 가능하게 한다. 이 정책은 페이지의 출처 밖에서 가져온 미디어 DOM 요소나 XHR 데이터를 읽기 위해 자바스크립트를 실행하는 것을 허용하지 않는다. 사이트의 스킴, 포트 번호, 호스트 이름의 집합은 출처로 식별된다. 상속 비보안 문제로 인해 JSONP는 CORS로 대체되고 있다.
좀 더 쉽게 알아보면, JSONP는 동일-출처 정책을 우회하면서 안전하게 데이터를 요청하기 위해 사용되는 방식입니다. domain이 flickr.com이 아닌 외부에서 Javascript로 다음과 같이 Flickr API를 호출하면 문제가 발생합니다.
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', 'https://www.flickr.com/services/feeds/photos_public.gne?format=json', true);
xmlhttp.onload = function () {
console.log('Retrieved Data: ' + xmlhttp.responseText);
};
xmlhttp.send(); // -> 교차 출처 요청 차단
만약 script 태그에 JSON 데이터를 직접적으로 삽입하면 JSON 데이터를 교차 출처 정책에 관계 없이 불러올 수 있습니다.
<script type="application/javascript" src="https://www.flickr.com/services/feeds/photos_public.gne?format=json">
</script>
하지만 JSON 형태로 데이터가 넘어온다면, src attribute에 block
이 들어가는 것으로 인식해서 문법 오류가 발생합니다.
그래서 파라미터로 API에 대한 callback 함수를 지정해서 JSON데이터를 감싸주도록 합니다.
<script type="application/javascript"
src="https://www.flickr.com/services/feeds/photos_public.gne?format=json&callback=jsonFlickrFeed">
</script>
jsonFlickrFeed({...})
이렇게 되면 문법오류가 발생하지 않고 API의 콜백함수로 지정된 jsonFlickrFeed()
에 파라미터로 JSON이 전달될 수 있습니다.
이렇게 CrossDomain 이슈를 해결하기 위한 장치였던 JSONP가! 저에게는 걸림돌이 되었습니다. 아무리 JSONP를 JSON으로 파싱하려 해봐도 Invalid JSON이라는 결과만이 따라왔기 때문이죠.
JSONDecoder를 통해서 JSONP를 JSON으로 변환하기는 어려웠습니다. (애초에 JSON이 아니니까요) 그래서 다음과 같은 방법들을 시도해봤습니다.
컨셉은 JSON으로 Decode 하기 전에 Data에서 callback함수 이름인 jsonFlickrFeed(
와 마지막 )
를 제거하는 거였습니다.
아주 불가능한 것은 아니었지만, 코드가 매—우 더러웠습니다. API 호출을 함에 있어서 파라미터로 JSONP여부와 콜백함수 이름을 넘겨야 한다는 것이 API 호출할 때 이미 그 결과를 알고 있어야 한다.는 의미와 같았기 때문입니다.
자 더럽한 코드는 잠깐 내려놓고, Flickr API docs를 탐독하기 시작합니다. 그리고 정답은 이미 Flickr Docs - JSON 응답형식에서 제공하고 있습니다.
콜백 함수
함수 래퍼가 없는 원본 JSON을 원하면 값이 1인 nojsoncallback 매개 변수를 요청에 추가하십시오.
사용자 고유의 콜백 함수 이름을 정의하려면 원하는 이름의 jsoncallback 매개 변수를 값으로 추가하십시오.
네… 오늘의 교훈… Docs를 제대로 읽자…………..
https://www.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1
그 결과는?
{
"title": "Uploads from everyone",
"link": "https://www.flickr.com/photos/",
"description": "",
"modified": "2019-07-01T10:00:00Z",
"generator": "https://www.flickr.com",
"items": [
{
"title": "Semana de la Dulzura",
"link": "https:\/\/www.flickr.com\/photos\/144395899@N06\/48170431461\/",
"media": {"m":"https:\/\/live.staticflickr.com\/65535\/48170431461_6d238cef55_m.jpg"},
"date_taken": "2019-07-01T11:20:12-08:00",
"description": " <p><a href=\"https:\/\/www.flickr.com\/people\/144395899@N06\/\">claudiazwenguer<\/a> posted a photo:<\/p> <p><a href=\"https:\/\/www.flickr.com\/photos\/144395899@N06\/48170431461\/\" title=\"Semana de la Dulzura\"><img src=\"https:\/\/live.staticflickr.com\/65535\/48170431461_6d238cef55_m.jpg\" width=\"216\" height=\"240\" alt=\"Semana de la Dulzura\" \/><\/a><\/p> ",
"published": "2019-07-01T15:00:13Z",
"author": "nobody@flickr.com (\"claudiazwenguer\")",
"author_id": "144395899@N06",
"tags": ""
}
]
}
네… 혼자 삽질했는데 JSONP JSON으로 바꿔보겠다고 시간날린게 아까워서 써봤습니다. 헤헿
WKNavigationDelegate 프로토콜의 메서드는 웹 뷰에서 탐색 요청을 수락하고, 불러오고 및 완료하는 과정에서 트리거되는 사용자 지정 동작을 구현하는 데 도움이됩니다.
protocol WKNavigationDelegate
웹뷰가 웹 컨텐츠를 수신하기 시작했을 때 호출됩니다.
func webView(WKWebView, didCommit: WKNavigation!)
웹 뷰에 웹 컨텐츠가 불러와지기 시작할 때 호출됩니다.
func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
웹뷰가 웹 컨텐츠를 수신하기 시작했을 때 호출됩니다.
func webView(WKWebView, didCommit: WKNavigation!)
웹 뷰가 서버 redirect를 수신했을 때 호출됩니다.
func webView(WKWebView, didReceiveServerRedirectForProvisionalNavigation: WKNavigation!)
웹 뷰가 인증 문제에 응답해야 할 때 호출됩니다.
func webView(WKWebView, didReceive: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
탐색도중 오류가 발생했을 때 호출됩니다.
func webView(WKWebView, didFail: WKNavigation!, withError: Error)
웹뷰가 컨텐츠를 불러오는 동안 오류가 발생했을 때 호출됩니다. func webView(WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error)
탐색이 완료되었을 때 호출됩니다.
func webView(WKWebView, didFinish: WKNavigation!)
웹 뷰의 웹 컨텐츠가 종료될 때 호출됩니다.
func webViewWebContentProcessDidTerminate(WKWebView)
탐색 취소를 허용할 것인지에 대해 결정합니다.
func webView(WKWebView, decidePolicyFor: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void)
응답이 수신된 후 탐색을 허용할지 아니면 취소할지 결정합니다.
func webView(WKWebView, decidePolicyFor: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void)
// webView(_:decidePolicyFor:decisionHandler:)에서 사용되는 정책 열거형
enum WKNavigationActionPolicy
// webView(_:decidePolicyFor:decisionHandler:)에서 사용되는 정책 열거형
enum WKNavigationResponsePolicy
cancel / allow
WKUIDelegate : 웹페이지를 대신해서 고유 사용자 인터페이스 요소들을 보여주는 메소드들을 제공합니다.
WKWindowFeatures 객체는 새 웹뷰가 요청될 때 포함 창에 대한 선택적 속성들을 지정합니다.
// 포함하는 윈도우의 재조정 할 크기. 혹은 resizability가 지정되어 있지 않으면 nil
var allowsResizing: NSNumber?
// 포함하는 창의 높이를 지정하는 CGFloat 값. 높이가 지정되지 않은 경우 nil.
var height: NSNumber?
// 포함하는 창의 너비를 지정하는 CGFloat 값. 너비를 지정하지 않으면 nil.
var width: NSNumber?
// 포함하는 창의 x 좌표를 지정하는 CGFloat 값이거나 x 좌표가 지정되지 않은 경우 nil입니다.
var x: NSNumber?
// 포함하는 창의 y 좌표를 지정하는 CGFloat 값이거나, y 좌표가 지정되지 않은 경우 nil입니다.
var y: NSNumber?
// 메뉴 바가 보여져야 하는지의 여부. 정해지지 않았으면 nil
var menuBarVisibility: NSNumber?
// 상태 바가 보여져야 하는지의 여부. 정해지지 않았으면 nil
var statusBarVisibility: NSNumber?
// tool 바가 보여져야 하는지의 여부. 정해지지 않았으면 nil
var toolbarsVisibility: NSNumber?
WKWebView를 초기화하는 데 필요한 속성들을 담는 객체입니다.
WKWebViewConfiguration 클래스를 사용하면 웹 페이지의 랜더링 속도, 미디어 재생 처리 방법, 사용자가 선택할 수 있는 항목의 세분화 및 기타 여러 옵션을 결정할 수 있습니다.
WKWebViewConfiguration은 WKWebView가 처음 초기화될 때에만 사용됩니다. WKWebView가 생성된 이후에는 이 클래스를 통해 속성들을 변경할 수 없습니다.
// user-agent 문자열에 사용된 애플리케이션의 이름
var applicationNameForUserAgent: String?
// 웹뷰가 사용하는 WKPreferences 객체
var preferences: WKPreferences
// 뷰의 웹 컨텐츠 프로세스를 포함하는 process pool
var processPool: WKProcessPool
// 웹 컨텍스트와 연결할 사용자 Content 컨트롤러
var userContentController: WKUserContentController
// 웹 뷰에서 사용한 웹 사이트 데이터 저장소
var websiteDataStore: WKWebsiteDataStore
// WKWebView 객체가 웹페이지의 크기 변경을 항상 허용해야 하는지 결정하는 Bool 값
// 기본값 : false
var ignoresViewportScaleLimits: Bool
// 웹 뷰가 컨텐츠 렌더링을 메모리에 완전히 로드될 때까지 기다릴지(억제할 지)에 대한 Bool 값
// 기본값 : false
var suppressesIncrementalRendering: Bool
// HTML5 동영상이 인라인으로 재생되는지 혹은 기본 전체 화면 컨트롤러를 사용하는지 여부를 나타내는 Bool 값
// 기본값 : false
var allowsInlineMediaPlayback: Bool
// AirPlay가 허용되는지 여부를 나타내는 Bool 값
// 기본값 : true
var allowsAirPlayForMediaPlayback: Bool
// HTML5 비디오가 picture-in-picture를 재생할 수 있는지 여부를 나타내는 Bool 값
// 기본값 : true
var allowsPictureInPictureMediaPlayback: Bool
// 사용자 제스처로 재생이 실행되어야 하는 미디어들
var mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes
// 사용자 제스처로 재생이 실행되어야 하는 미디어 타입들
struct WKAudiovisualMediaTypes
The media types that require a user gesture to begin playing
// 사용자가 웹 뷰에서 컨텐츠를 선택할 수 있는 세분화 레벨
var selectionGranularity: WKSelectionGranularity
// 사용자가 웹 뷰에서 컨텐츠를 선택할 수 있는 세분화 레벨
enum WKSelectionGranularity
The granularity with which a selection can be created and modified interactively.
WKSelectionGranularity
- character : 문자 영역을 선택할 수 있습니다.
- dynamic : 선택 가능한 레벨이 동적으로 달라집니다.
// 사용자 인터페이스 요소들의 방향
var userInterfaceDirectionPolicy: WKUserInterfaceDirectionPolicy
// 웹 뷰에서 사용자 인터페이스 요소들의 방향을 결정하는 데 사용되는 정책
enum WKUserInterfaceDirectionPolicy
WKUserInterfaceDirectionPolicy
- content: CSS/HTML/XHTML 규격을 따른다. 기본값
- system: 뷰의 userInterfaceLayoutDirection 속석을 따른다.
// 예상되는 데이터 검색 유형
var dataDetectorTypes: WKDataDetectorTypes
The type of data detection desired.
// 발견된 데이터 유형을 나타내는 구조체
// 전화번호, URL, 주소, 캘린더 이벤트, 운송번호, 항공번호 등
struct WKDataDetectorTypes
// 주어진 URL 스키마에 사용될 위한 URL 스키마 핸들러를 추가합니다.
func setURLSchemeHandler(WKURLSchemeHandler?, forURLScheme: String)
// 주어진 URL 스키마에 현재 등록되어있는 스키마 핸들러를 반환합니다.
func urlSchemeHandler(forURLScheme: String) -> WKURLSchemeHandler?