diff --git a/404.html b/404.html new file mode 100644 index 0000000..bd7cac0 --- /dev/null +++ b/404.html @@ -0,0 +1,37 @@ + + + + + + + + + iOS Training + + + + + +

404

There's nothing here.
Take me home
+ + + diff --git a/android-icon-144x144.png b/android-icon-144x144.png new file mode 100644 index 0000000..8424bd2 Binary files /dev/null and b/android-icon-144x144.png differ diff --git a/android-icon-192x192.png b/android-icon-192x192.png new file mode 100644 index 0000000..e51017e Binary files /dev/null and b/android-icon-192x192.png differ diff --git a/android-icon-36x36.png b/android-icon-36x36.png new file mode 100644 index 0000000..75056ee Binary files /dev/null and b/android-icon-36x36.png differ diff --git a/android-icon-48x48.png b/android-icon-48x48.png new file mode 100644 index 0000000..0747662 Binary files /dev/null and b/android-icon-48x48.png differ diff --git a/android-icon-72x72.png b/android-icon-72x72.png new file mode 100644 index 0000000..a011d7c Binary files /dev/null and b/android-icon-72x72.png differ diff --git a/android-icon-96x96.png b/android-icon-96x96.png new file mode 100644 index 0000000..5edb9b9 Binary files /dev/null and b/android-icon-96x96.png differ diff --git a/api-communication/index.html b/api-communication/index.html new file mode 100644 index 0000000..14182ef --- /dev/null +++ b/api-communication/index.html @@ -0,0 +1,53 @@ + + + + + + + + + Communicate with a REST API | iOS Training + + + + + +

Communicate with a REST API

Estimated time

1/4 day

Some useful concepts

Communicating with a REST API relies on multiple concepts that we'll cover briefly below:

  • JSONopen in new window: a standard data interchange format used a lot in the HTTP messages of REST APIs.
  • REST APIopen in new window: it is a standard communication interface between a client and conforms to the constraints of REST architectural style. In a REST API, HTTP messages are stateless and use JSON data format.
  • HTTP messagesopen in new window: an http message is a textual message that contains different parts and can be either a request or a response. A request is the HTTP message that the client sends to the server and response is the HTTP message that the server sends to the client in reaction to the request. Both requests and responses have a part called a body. In rest APIs, the body is generally formatted in JSON.
  • Codableopen in new window: it is a type that can convert itself into and out of an external representation. It is equivalent to Serializable in Java. This type is helpful if we want to convert an object into an out of a JSON string.
  • async and await: these keywords are used to call an asynchronous function using a synchronous coding fashion. This means that callbacks are needed no more!
  • URLSessionopen in new window: The official iOS HTTP client which is part of the Foundation library. This library is not part of the Swift standard library but there is an implementation for non-Apple platforms which is called swift-corelibs-foundationopen in new window.

PW: call a REST API

This PW relies on the excellent tutorial from hackingwithswiftopen in new window. It guides you on how to fetch JSON data from iTunes's APIopen in new window. Please find and excerpt of the response body below.

{
+   "resultCount":50,
+   "results":[
+      {
+         "wrapperType":"track",
+         "kind":"song",
+         "artistId":159260351,
+         "collectionId":1440913923,
+         "trackId":1440914010,
+         "artistName":"Taylor Swift",
+         "collectionName":"Taylor Swift (Bonus Track Version)",
+         "trackName":"Our Song",
+         "collectionCensoredName":"Taylor Swift (Bonus Track Version)",
+      }
+   ]
+}
+
+ + + diff --git a/apple-icon-114x114.png b/apple-icon-114x114.png new file mode 100644 index 0000000..2e47686 Binary files /dev/null and b/apple-icon-114x114.png differ diff --git a/apple-icon-120x120.png b/apple-icon-120x120.png new file mode 100644 index 0000000..ea9523d Binary files /dev/null and b/apple-icon-120x120.png differ diff --git a/apple-icon-144x144.png b/apple-icon-144x144.png new file mode 100644 index 0000000..8424bd2 Binary files /dev/null and b/apple-icon-144x144.png differ diff --git a/apple-icon-152x152.png b/apple-icon-152x152.png new file mode 100644 index 0000000..e6dd5ee Binary files /dev/null and b/apple-icon-152x152.png differ diff --git a/apple-icon-180x180.png b/apple-icon-180x180.png new file mode 100644 index 0000000..815d6d3 Binary files /dev/null and b/apple-icon-180x180.png differ diff --git a/apple-icon-57x57.png b/apple-icon-57x57.png new file mode 100644 index 0000000..c1f0cd7 Binary files /dev/null and b/apple-icon-57x57.png differ diff --git a/apple-icon-60x60.png b/apple-icon-60x60.png new file mode 100644 index 0000000..d909e37 Binary files /dev/null and b/apple-icon-60x60.png differ diff --git a/apple-icon-72x72.png b/apple-icon-72x72.png new file mode 100644 index 0000000..a011d7c Binary files /dev/null and b/apple-icon-72x72.png differ diff --git a/apple-icon-76x76.png b/apple-icon-76x76.png new file mode 100644 index 0000000..bc81b68 Binary files /dev/null and b/apple-icon-76x76.png differ diff --git a/apple-icon-precomposed.png b/apple-icon-precomposed.png new file mode 100644 index 0000000..9feacea Binary files /dev/null and b/apple-icon-precomposed.png differ diff --git a/apple-icon.png b/apple-icon.png new file mode 100644 index 0000000..9feacea Binary files /dev/null and b/apple-icon.png differ diff --git a/assets/404.html-9pEcDQrh.js b/assets/404.html-9pEcDQrh.js new file mode 100644 index 0000000..09a5667 --- /dev/null +++ b/assets/404.html-9pEcDQrh.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as o,a}from"./app-Bbun9eEO.js";const n={},c=a("p",null,"404 Not Found",-1),l=[c];function s(_,r){return e(),o("div",null,l)}const d=t(n,[["render",s],["__file","404.html.vue"]]),h=JSON.parse('{"path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,h as data}; diff --git a/assets/app-Bbun9eEO.js b/assets/app-Bbun9eEO.js new file mode 100644 index 0000000..47ea131 --- /dev/null +++ b/assets/app-Bbun9eEO.js @@ -0,0 +1,26 @@ +/** +* @vue/shared v3.4.22 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**//*! #__NO_SIDE_EFFECTS__ */function Ro(e,t){const n=new Set(e.split(","));return t?r=>n.has(r.toLowerCase()):r=>n.has(r)}const be={},un=[],Ze=()=>{},ua=()=>!1,Gn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Oo=e=>e.startsWith("onUpdate:"),Pe=Object.assign,Io=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},fa=Object.prototype.hasOwnProperty,de=(e,t)=>fa.call(e,t),ee=Array.isArray,fn=e=>Ir(e)==="[object Map]",Ci=e=>Ir(e)==="[object Set]",le=e=>typeof e=="function",Ae=e=>typeof e=="string",nn=e=>typeof e=="symbol",we=e=>e!==null&&typeof e=="object",Si=e=>(we(e)||le(e))&&le(e.then)&&le(e.catch),xi=Object.prototype.toString,Ir=e=>xi.call(e),da=e=>Ir(e).slice(8,-1),Li=e=>Ir(e)==="[object Object]",$o=e=>Ae(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,dn=Ro(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),$r=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},ha=/-(\w)/g,tt=$r(e=>e.replace(ha,(t,n)=>n?n.toUpperCase():"")),pa=/\B([A-Z])/g,rn=$r(e=>e.replace(pa,"-$1").toLowerCase()),Yn=$r(e=>e.charAt(0).toUpperCase()+e.slice(1)),Yr=$r(e=>e?`on${Yn(e)}`:""),Ht=(e,t)=>!Object.is(e,t),Jr=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},ma=e=>{const t=parseFloat(e);return isNaN(t)?e:t},ga=e=>{const t=Ae(e)?Number(e):NaN;return isNaN(t)?e:t};let us;const Ai=()=>us||(us=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Jn(e){if(ee(e)){const t={};for(let n=0;n{if(n){const r=n.split(ya);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Ke(e){let t="";if(Ae(e))t=e;else if(ee(e))for(let n=0;nAe(e)?e:e==null?"":ee(e)||we(e)&&(e.toString===xi||!le(e.toString))?JSON.stringify(e,Pi,2):String(e),Pi=(e,t)=>t&&t.__v_isRef?Pi(e,t.value):fn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o],s)=>(n[Xr(r,s)+" =>"]=o,n),{})}:Ci(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Xr(n))}:nn(t)?Xr(t):we(t)&&!ee(t)&&!Li(t)?String(t):t,Xr=(e,t="")=>{var n;return nn(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.4.22 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Ye;class Ca{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Ye,!t&&Ye&&(this.index=(Ye.scopes||(Ye.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Ye;try{return Ye=this,t()}finally{Ye=n}}}on(){Ye=this}off(){Ye=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n=4))break}this._dirtyLevel===1&&(this._dirtyLevel=0),jt()}return this._dirtyLevel>=4}set dirty(t){this._dirtyLevel=t?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=$t,n=Zt;try{return $t=!0,Zt=this,this._runnings++,fs(this),this.fn()}finally{ds(this),this._runnings--,Zt=n,$t=t}}stop(){var t;this.active&&(fs(this),ds(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function La(e){return e.value}function fs(e){e._trackId++,e._depsLength=0}function ds(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},Er=new WeakMap,en=Symbol(""),go=Symbol("");function qe(e,t,n){if($t&&Zt){let r=Er.get(e);r||Er.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=Mi(()=>r.delete(n))),$i(Zt,o)}}function bt(e,t,n,r,o,s){const i=Er.get(e);if(!i)return;let l=[];if(t==="clear")l=[...i.values()];else if(n==="length"&&ee(e)){const a=Number(r);i.forEach((c,u)=>{(u==="length"||!nn(u)&&u>=a)&&l.push(c)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":ee(e)?$o(n)&&l.push(i.get("length")):(l.push(i.get(en)),fn(e)&&l.push(i.get(go)));break;case"delete":ee(e)||(l.push(i.get(en)),fn(e)&&l.push(i.get(go)));break;case"set":fn(e)&&l.push(i.get(en));break}Mo();for(const a of l)a&&Ni(a,4);Ho()}function ka(e,t){var n;return(n=Er.get(e))==null?void 0:n.get(t)}const Aa=Ro("__proto__,__v_isRef,__isVue"),Hi=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(nn)),hs=Ta();function Ta(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=he(this);for(let s=0,i=this.length;s{e[t]=function(...n){zt(),Mo();const r=he(this)[t].apply(this,n);return Ho(),jt(),r}}),e}function Pa(e){nn(e)||(e=String(e));const t=he(this);return qe(t,"has",e),t.hasOwnProperty(e)}class Fi{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){const o=this._isReadonly,s=this._isShallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return s;if(n==="__v_raw")return r===(o?s?Ua:ji:s?zi:Di).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const i=ee(t);if(!o){if(i&&de(hs,n))return Reflect.get(hs,n,r);if(n==="hasOwnProperty")return Pa}const l=Reflect.get(t,n,r);return(nn(n)?Hi.has(n):Aa(n))||(o||qe(t,"get",n),s)?l:je(l)?i&&$o(n)?l:l.value:we(l)?o?Mr(l):Xn(l):l}}class Bi extends Fi{constructor(t=!1){super(!1,t)}set(t,n,r,o){let s=t[n];if(!this._isShallow){const a=Hn(s);if(!Cr(r)&&!Hn(r)&&(s=he(s),r=he(r)),!ee(t)&&je(s)&&!je(r))return a?!1:(s.value=r,!0)}const i=ee(t)&&$o(n)?Number(n)e,Nr=e=>Reflect.getPrototypeOf(e);function sr(e,t,n=!1,r=!1){e=e.__v_raw;const o=he(e),s=he(t);n||(Ht(t,s)&&qe(o,"get",t),qe(o,"get",s));const{has:i}=Nr(o),l=r?Fo:n?zo:Fn;if(i.call(o,t))return l(e.get(t));if(i.call(o,s))return l(e.get(s));e!==o&&e.get(t)}function ir(e,t=!1){const n=this.__v_raw,r=he(n),o=he(e);return t||(Ht(e,o)&&qe(r,"has",e),qe(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function lr(e,t=!1){return e=e.__v_raw,!t&&qe(he(e),"iterate",en),Reflect.get(e,"size",e)}function ps(e){e=he(e);const t=he(this);return Nr(t).has.call(t,e)||(t.add(e),bt(t,"add",e,e)),this}function ms(e,t){t=he(t);const n=he(this),{has:r,get:o}=Nr(n);let s=r.call(n,e);s||(e=he(e),s=r.call(n,e));const i=o.call(n,e);return n.set(e,t),s?Ht(t,i)&&bt(n,"set",e,t):bt(n,"add",e,t),this}function gs(e){const t=he(this),{has:n,get:r}=Nr(t);let o=n.call(t,e);o||(e=he(e),o=n.call(t,e)),r&&r.call(t,e);const s=t.delete(e);return o&&bt(t,"delete",e,void 0),s}function vs(){const e=he(this),t=e.size!==0,n=e.clear();return t&&bt(e,"clear",void 0,void 0),n}function ar(e,t){return function(r,o){const s=this,i=s.__v_raw,l=he(i),a=t?Fo:e?zo:Fn;return!e&&qe(l,"iterate",en),i.forEach((c,u)=>r.call(o,a(c),a(u),s))}}function cr(e,t,n){return function(...r){const o=this.__v_raw,s=he(o),i=fn(s),l=e==="entries"||e===Symbol.iterator&&i,a=e==="keys"&&i,c=o[e](...r),u=n?Fo:t?zo:Fn;return!t&&qe(s,"iterate",a?go:en),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:l?[u(f[0]),u(f[1])]:u(f),done:d}},[Symbol.iterator](){return this}}}}function xt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Na(){const e={get(s){return sr(this,s)},get size(){return lr(this)},has:ir,add:ps,set:ms,delete:gs,clear:vs,forEach:ar(!1,!1)},t={get(s){return sr(this,s,!1,!0)},get size(){return lr(this)},has:ir,add:ps,set:ms,delete:gs,clear:vs,forEach:ar(!1,!0)},n={get(s){return sr(this,s,!0)},get size(){return lr(this,!0)},has(s){return ir.call(this,s,!0)},add:xt("add"),set:xt("set"),delete:xt("delete"),clear:xt("clear"),forEach:ar(!0,!1)},r={get(s){return sr(this,s,!0,!0)},get size(){return lr(this,!0)},has(s){return ir.call(this,s,!0)},add:xt("add"),set:xt("set"),delete:xt("delete"),clear:xt("clear"),forEach:ar(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=cr(s,!1,!1),n[s]=cr(s,!0,!1),t[s]=cr(s,!1,!0),r[s]=cr(s,!0,!0)}),[e,n,t,r]}const[Ma,Ha,Fa,Ba]=Na();function Bo(e,t){const n=t?e?Ba:Fa:e?Ha:Ma;return(r,o,s)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(de(n,o)&&o in r?n:r,o,s)}const Da={get:Bo(!1,!1)},za={get:Bo(!1,!0)},ja={get:Bo(!0,!1)},Di=new WeakMap,zi=new WeakMap,ji=new WeakMap,Ua=new WeakMap;function Wa(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Va(e){return e.__v_skip||!Object.isExtensible(e)?0:Wa(da(e))}function Xn(e){return Hn(e)?e:Do(e,!1,Oa,Da,Di)}function Ui(e){return Do(e,!1,$a,za,zi)}function Mr(e){return Do(e,!0,Ia,ja,ji)}function Do(e,t,n,r,o){if(!we(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=o.get(e);if(s)return s;const i=Va(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return o.set(e,l),l}function Tn(e){return Hn(e)?Tn(e.__v_raw):!!(e&&e.__v_isReactive)}function Hn(e){return!!(e&&e.__v_isReadonly)}function Cr(e){return!!(e&&e.__v_isShallow)}function Wi(e){return e?!!e.__v_raw:!1}function he(e){const t=e&&e.__v_raw;return t?he(t):e}function Ka(e){return Object.isExtensible(e)&&ki(e,"__v_skip",!0),e}const Fn=e=>we(e)?Xn(e):e,zo=e=>we(e)?Mr(e):e;class Vi{constructor(t,n,r,o){this.getter=t,this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new No(()=>t(this._value),()=>Pn(this,this.effect._dirtyLevel===2?2:3)),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=he(this);return(!t._cacheable||t.effect.dirty)&&Ht(t._value,t._value=t.effect.run())&&Pn(t,4),jo(t),t.effect._dirtyLevel>=2&&Pn(t,2),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function qa(e,t,n=!1){let r,o;const s=le(e);return s?(r=e,o=Ze):(r=e.get,o=e.set),new Vi(r,o,s||!o,n)}function jo(e){var t;$t&&Zt&&(e=he(e),$i(Zt,(t=e.dep)!=null?t:e.dep=Mi(()=>e.dep=void 0,e instanceof Vi?e:void 0)))}function Pn(e,t=4,n){e=he(e);const r=e.dep;r&&Ni(r,t)}function je(e){return!!(e&&e.__v_isRef===!0)}function ce(e){return Ki(e,!1)}function on(e){return Ki(e,!0)}function Ki(e,t){return je(e)?e:new Ga(e,t)}class Ga{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:he(t),this._value=n?t:Fn(t)}get value(){return jo(this),this._value}set value(t){const n=this.__v_isShallow||Cr(t)||Hn(t);t=n?t:he(t),Ht(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Fn(t),Pn(this,4))}}function X(e){return je(e)?e.value:e}const Ya={get:(e,t,n)=>X(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return je(o)&&!je(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function qi(e){return Tn(e)?e:new Proxy(e,Ya)}class Ja{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>jo(this),()=>Pn(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Xa(e){return new Ja(e)}function Hr(e){const t=ee(e)?new Array(e.length):{};for(const n in e)t[n]=Za(e,n);return t}class Qa{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return ka(he(this._object),this._key)}}function Za(e,t,n){const r=e[t];return je(r)?r:new Qa(e,t,n)}/** +* @vue/runtime-core v3.4.22 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Nt(e,t,n,r){try{return r?e(...r):e()}catch(o){Qn(o,t,n)}}function et(e,t,n,r){if(le(e)){const o=Nt(e,t,n,r);return o&&Si(o)&&o.catch(s=>{Qn(s,t,n)}),o}if(ee(e)){const o=[];for(let s=0;s>>1,o=De[r],s=Dn(o);sht&&De.splice(t,1)}function rc(e){ee(e)?hn.push(...e):(!Tt||!Tt.includes(e,e.allowRecurse?Yt+1:Yt))&&hn.push(e),Yi()}function ys(e,t,n=Bn?ht+1:0){for(;nDn(n)-Dn(r));if(hn.length=0,Tt){Tt.push(...t);return}for(Tt=t,Yt=0;Yte.id==null?1/0:e.id,oc=(e,t)=>{const n=Dn(e)-Dn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ji(e){vo=!1,Bn=!0,De.sort(oc);try{for(ht=0;htAe(m)?m.trim():m)),f&&(o=n.map(ma))}let l,a=r[l=Yr(t)]||r[l=Yr(tt(t))];!a&&s&&(a=r[l=Yr(rn(t))]),a&&et(a,e,6,o);const c=r[l+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,et(c,e,6,o)}}function Xi(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const s=e.emits;let i={},l=!1;if(!le(e)){const a=c=>{const u=Xi(c,t,!0);u&&(l=!0,Pe(i,u))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!s&&!l?(we(e)&&r.set(e,null),null):(ee(s)?s.forEach(a=>i[a]=null):Pe(i,s),we(e)&&r.set(e,i),i)}function Br(e,t){return!e||!Gn(t)?!1:(t=t.slice(2).replace(/Once$/,""),de(e,t[0].toLowerCase()+t.slice(1))||de(e,rn(t))||de(e,t))}let Te=null,Qi=null;function xr(e){const t=Te;return Te=e,Qi=e&&e.type.__scopeId||null,t}function Le(e,t=Te,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Ps(-1);const s=xr(t);let i;try{i=e(...o)}finally{xr(s),r._d&&Ps(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Qr(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:s,propsOptions:[i],slots:l,attrs:a,emit:c,render:u,renderCache:f,data:d,setupState:m,ctx:g,inheritAttrs:b}=e;let E,A;const T=xr(e);try{if(n.shapeFlag&4){const C=o||r,B=C;E=ot(u.call(B,C,f,s,m,d,g)),A=a}else{const C=t;E=ot(C.length>1?C(s,{attrs:a,slots:l,emit:c}):C(s,null)),A=t.props?a:ic(a)}}catch(C){$n.length=0,Qn(C,e,1),E=se(Je)}let v=E;if(A&&b!==!1){const C=Object.keys(A),{shapeFlag:B}=v;C.length&&B&7&&(i&&C.some(Oo)&&(A=lc(A,i)),v=Bt(v,A))}return n.dirs&&(v=Bt(v),v.dirs=v.dirs?v.dirs.concat(n.dirs):n.dirs),n.transition&&(v.transition=n.transition),E=v,xr(T),E}const ic=e=>{let t;for(const n in e)(n==="class"||n==="style"||Gn(n))&&((t||(t={}))[n]=e[n]);return t},lc=(e,t)=>{const n={};for(const r in e)(!Oo(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function ac(e,t,n){const{props:r,children:o,component:s}=e,{props:i,children:l,patchFlag:a}=t,c=s.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return r?_s(r,i,c):!!i;if(a&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function el(e,t){t&&t.pendingBranch?ee(e)?t.effects.push(...e):t.effects.push(e):rc(e)}const hc=Symbol.for("v-scx"),pc=()=>ze(hc);function mc(e,t){return Wo(e,null,t)}const ur={};function Fe(e,t,n){return Wo(e,t,n)}function Wo(e,t,{immediate:n,deep:r,flush:o,once:s,onTrack:i,onTrigger:l}=be){if(t&&s){const O=t;t=(...H)=>{O(...H),B()}}const a=$e,c=O=>r===!0?O:Xt(O,r===!1?1:void 0);let u,f=!1,d=!1;if(je(e)?(u=()=>e.value,f=Cr(e)):Tn(e)?(u=()=>c(e),f=!0):ee(e)?(d=!0,f=e.some(O=>Tn(O)||Cr(O)),u=()=>e.map(O=>{if(je(O))return O.value;if(Tn(O))return c(O);if(le(O))return Nt(O,a,2)})):le(e)?t?u=()=>Nt(e,a,2):u=()=>(m&&m(),et(e,a,3,[g])):u=Ze,t&&r){const O=u;u=()=>Xt(O())}let m,g=O=>{m=v.onStop=()=>{Nt(O,a,4),m=v.onStop=void 0}},b;if(tr)if(g=Ze,t?n&&et(t,a,3,[u(),d?[]:void 0,g]):u(),o==="sync"){const O=pc();b=O.__watcherHandles||(O.__watcherHandles=[])}else return Ze;let E=d?new Array(e.length).fill(ur):ur;const A=()=>{if(!(!v.active||!v.dirty))if(t){const O=v.run();(r||f||(d?O.some((H,y)=>Ht(H,E[y])):Ht(O,E)))&&(m&&m(),et(t,a,3,[O,E===ur?void 0:d&&E[0]===ur?[]:E,g]),E=O)}else v.run()};A.allowRecurse=!!t;let T;o==="sync"?T=A:o==="post"?T=()=>Ve(A,a&&a.suspense):(A.pre=!0,a&&(A.id=a.uid),T=()=>Fr(A));const v=new No(u,Ze,T),C=Ri(),B=()=>{v.stop(),C&&Io(C.effects,v)};return t?n?A():E=v.run():o==="post"?Ve(v.run.bind(v),a&&a.suspense):v.run(),b&&b.push(B),B}function gc(e,t,n){const r=this.proxy,o=Ae(e)?e.includes(".")?tl(r,e):()=>r[e]:e.bind(r,r);let s;le(t)?s=t:(s=t.handler,n=t);const i=er(this),l=Wo(o,s.bind(r),n);return i(),l}function tl(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o0){if(n>=t)return e;n++}if(r=r||new Set,r.has(e))return e;if(r.add(e),je(e))Xt(e.value,t,n,r);else if(ee(e))for(let o=0;o{Xt(o,t,n,r)});else if(Li(e))for(const o in e)Xt(e[o],t,n,r);return e}function Lr(e,t){if(Te===null)return e;const n=Wr(Te)||Te.proxy,r=e.dirs||(e.dirs=[]);for(let o=0;o{e.isMounted=!0}),zr(()=>{e.isUnmounting=!0}),e}const Xe=[Function,Array],nl={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Xe,onEnter:Xe,onAfterEnter:Xe,onEnterCancelled:Xe,onBeforeLeave:Xe,onLeave:Xe,onAfterLeave:Xe,onLeaveCancelled:Xe,onBeforeAppear:Xe,onAppear:Xe,onAfterAppear:Xe,onAppearCancelled:Xe},yc={name:"BaseTransition",props:nl,setup(e,{slots:t}){const n=Ur(),r=vc();return()=>{const o=t.default&&ol(t.default(),!0);if(!o||!o.length)return;let s=o[0];if(o.length>1){for(const d of o)if(d.type!==Je){s=d;break}}const i=he(e),{mode:l}=i;if(r.isLeaving)return Zr(s);const a=ws(s);if(!a)return Zr(s);const c=yo(a,i,r,n);_o(a,c);const u=n.subTree,f=u&&ws(u);if(f&&f.type!==Je&&!Jt(a,f)){const d=yo(f,i,r,n);if(_o(f,d),l==="out-in")return r.isLeaving=!0,d.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},Zr(s);l==="in-out"&&a.type!==Je&&(d.delayLeave=(m,g,b)=>{const E=rl(r,f);E[String(f.key)]=f,m[Pt]=()=>{g(),m[Pt]=void 0,delete c.delayedLeave},c.delayedLeave=b})}return s}}},_c=yc;function rl(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function yo(e,t,n,r){const{appear:o,mode:s,persisted:i=!1,onBeforeEnter:l,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:d,onAfterLeave:m,onLeaveCancelled:g,onBeforeAppear:b,onAppear:E,onAfterAppear:A,onAppearCancelled:T}=t,v=String(e.key),C=rl(n,e),B=(y,G)=>{y&&et(y,r,9,G)},O=(y,G)=>{const L=G[1];B(y,G),ee(y)?y.every(W=>W.length<=1)&&L():y.length<=1&&L()},H={mode:s,persisted:i,beforeEnter(y){let G=l;if(!n.isMounted)if(o)G=b||l;else return;y[Pt]&&y[Pt](!0);const L=C[v];L&&Jt(e,L)&&L.el[Pt]&&L.el[Pt](),B(G,[y])},enter(y){let G=a,L=c,W=u;if(!n.isMounted)if(o)G=E||a,L=A||c,W=T||u;else return;let w=!1;const M=y[fr]=te=>{w||(w=!0,te?B(W,[y]):B(L,[y]),H.delayedLeave&&H.delayedLeave(),y[fr]=void 0)};G?O(G,[y,M]):M()},leave(y,G){const L=String(e.key);if(y[fr]&&y[fr](!0),n.isUnmounting)return G();B(f,[y]);let W=!1;const w=y[Pt]=M=>{W||(W=!0,G(),M?B(g,[y]):B(m,[y]),y[Pt]=void 0,C[L]===e&&delete C[L])};C[L]=e,d?O(d,[y,w]):w()},clone(y){return yo(y,t,n,r)}};return H}function Zr(e){if(Zn(e))return e=Bt(e),e.children=null,e}function ws(e){return Zn(e)?e.children?e.children[0]:void 0:e}function _o(e,t){e.shapeFlag&6&&e.component?_o(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function ol(e,t=!1,n){let r=[],o=0;for(let s=0;s1)for(let s=0;s!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function bc(e){le(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:s,suspensible:i=!0,onError:l}=e;let a=null,c,u=0;const f=()=>(u++,a=null,d()),d=()=>{let m;return a||(m=a=t().catch(g=>{if(g=g instanceof Error?g:new Error(String(g)),l)return new Promise((b,E)=>{l(g,()=>b(f()),()=>E(g),u+1)});throw g}).then(g=>m!==a&&a?a:(g&&(g.__esModule||g[Symbol.toStringTag]==="Module")&&(g=g.default),c=g,g)))};return pe({name:"AsyncComponentWrapper",__asyncLoader:d,get __asyncResolved(){return c},setup(){const m=$e;if(c)return()=>eo(c,m);const g=T=>{a=null,Qn(T,m,13,!r)};if(i&&m.suspense||tr)return d().then(T=>()=>eo(T,m)).catch(T=>(g(T),()=>r?se(r,{error:T}):null));const b=ce(!1),E=ce(),A=ce(!!o);return o&&setTimeout(()=>{A.value=!1},o),s!=null&&setTimeout(()=>{if(!b.value&&!E.value){const T=new Error(`Async component timed out after ${s}ms.`);g(T),E.value=T}},s),d().then(()=>{b.value=!0,m.parent&&Zn(m.parent.vnode)&&(m.parent.effect.dirty=!0,Fr(m.parent.update))}).catch(T=>{g(T),E.value=T}),()=>{if(b.value&&c)return eo(c,m);if(E.value&&r)return se(r,{error:E.value});if(n&&!A.value)return se(n)}}})}function eo(e,t){const{ref:n,props:r,children:o,ce:s}=t.vnode,i=se(e,r,o);return i.ref=n,i.ce=s,delete t.vnode.ce,i}const Zn=e=>e.type.__isKeepAlive;function wc(e,t){sl(e,"a",t)}function Ec(e,t){sl(e,"da",t)}function sl(e,t,n=$e){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(Dr(t,r,n),n){let o=n.parent;for(;o&&o.parent;)Zn(o.parent.vnode)&&Cc(r,t,n,o),o=o.parent}}function Cc(e,t,n,r){const o=Dr(t,e,r,!0);jr(()=>{Io(r[t],o)},n)}function Dr(e,t,n=$e,r=!1){if(n){const o=n[e]||(n[e]=[]),s=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;zt();const l=er(n),a=et(t,n,e,i);return l(),jt(),a});return r?o.unshift(s):o.push(s),s}}const Et=e=>(t,n=$e)=>(!tr||e==="sp")&&Dr(e,(...r)=>t(...r),n),Sc=Et("bm"),Me=Et("m"),xc=Et("bu"),Lc=Et("u"),zr=Et("bum"),jr=Et("um"),kc=Et("sp"),Ac=Et("rtg"),Tc=Et("rtc");function Pc(e,t=$e){Dr("ec",e,t)}function Ft(e,t,n,r){let o;const s=n&&n[r];if(ee(e)||Ae(e)){o=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,s&&s[l]));else{const i=Object.keys(e);o=new Array(i.length);for(let l=0,a=i.length;lTr(t)?!(t.type===Je||t.type===_e&&!il(t.children)):!0)?e:null}const bo=e=>e?bl(e)?Wr(e)||e.proxy:bo(e.parent):null,Rn=Pe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>bo(e.parent),$root:e=>bo(e.root),$emit:e=>e.emit,$options:e=>Vo(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,Fr(e.update)}),$nextTick:e=>e.n||(e.n=_n.bind(e.proxy)),$watch:e=>gc.bind(e)}),to=(e,t)=>e!==be&&!e.__isScriptSetup&&de(e,t),Rc={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:n,setupState:r,data:o,props:s,accessCache:i,type:l,appContext:a}=e;let c;if(t[0]!=="$"){const m=i[t];if(m!==void 0)switch(m){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return s[t]}else{if(to(r,t))return i[t]=1,r[t];if(o!==be&&de(o,t))return i[t]=2,o[t];if((c=e.propsOptions[0])&&de(c,t))return i[t]=3,s[t];if(n!==be&&de(n,t))return i[t]=4,n[t];wo&&(i[t]=0)}}const u=Rn[t];let f,d;if(u)return t==="$attrs"&&qe(e,"get",t),u(e);if((f=l.__cssModules)&&(f=f[t]))return f;if(n!==be&&de(n,t))return i[t]=4,n[t];if(d=a.config.globalProperties,de(d,t))return d[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:s}=e;return to(o,t)?(o[t]=n,!0):r!==be&&de(r,t)?(r[t]=n,!0):de(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(s[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:s}},i){let l;return!!n[i]||e!==be&&de(e,i)||to(t,i)||(l=s[0])&&de(l,i)||de(r,i)||de(Rn,i)||de(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:de(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Es(e){return ee(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let wo=!0;function Oc(e){const t=Vo(e),n=e.proxy,r=e.ctx;wo=!1,t.beforeCreate&&Cs(t.beforeCreate,e,"bc");const{data:o,computed:s,methods:i,watch:l,provide:a,inject:c,created:u,beforeMount:f,mounted:d,beforeUpdate:m,updated:g,activated:b,deactivated:E,beforeDestroy:A,beforeUnmount:T,destroyed:v,unmounted:C,render:B,renderTracked:O,renderTriggered:H,errorCaptured:y,serverPrefetch:G,expose:L,inheritAttrs:W,components:w,directives:M,filters:te}=t;if(c&&Ic(c,r,null),i)for(const Y in i){const V=i[Y];le(V)&&(r[Y]=V.bind(n))}if(o){const Y=o.call(n,n);we(Y)&&(e.data=Xn(Y))}if(wo=!0,s)for(const Y in s){const V=s[Y],Re=le(V)?V.bind(n,n):le(V.get)?V.get.bind(n,n):Ze,He=!le(V)&&le(V.set)?V.set.bind(n):Ze,We=I({get:Re,set:He});Object.defineProperty(r,Y,{enumerable:!0,configurable:!0,get:()=>We.value,set:Be=>We.value=Be})}if(l)for(const Y in l)ll(l[Y],r,n,Y);if(a){const Y=le(a)?a.call(n):a;Reflect.ownKeys(Y).forEach(V=>{Mt(V,Y[V])})}u&&Cs(u,e,"c");function R(Y,V){ee(V)?V.forEach(Re=>Y(Re.bind(n))):V&&Y(V.bind(n))}if(R(Sc,f),R(Me,d),R(xc,m),R(Lc,g),R(wc,b),R(Ec,E),R(Pc,y),R(Tc,O),R(Ac,H),R(zr,T),R(jr,C),R(kc,G),ee(L))if(L.length){const Y=e.exposed||(e.exposed={});L.forEach(V=>{Object.defineProperty(Y,V,{get:()=>n[V],set:Re=>n[V]=Re})})}else e.exposed||(e.exposed={});B&&e.render===Ze&&(e.render=B),W!=null&&(e.inheritAttrs=W),w&&(e.components=w),M&&(e.directives=M)}function Ic(e,t,n=Ze){ee(e)&&(e=Eo(e));for(const r in e){const o=e[r];let s;we(o)?"default"in o?s=ze(o.from||r,o.default,!0):s=ze(o.from||r):s=ze(o),je(s)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>s.value,set:i=>s.value=i}):t[r]=s}}function Cs(e,t,n){et(ee(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function ll(e,t,n,r){const o=r.includes(".")?tl(n,r):()=>n[r];if(Ae(e)){const s=t[e];le(s)&&Fe(o,s)}else if(le(e))Fe(o,e.bind(n));else if(we(e))if(ee(e))e.forEach(s=>ll(s,t,n,r));else{const s=le(e.handler)?e.handler.bind(n):t[e.handler];le(s)&&Fe(o,s,e)}}function Vo(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:s,config:{optionMergeStrategies:i}}=e.appContext,l=s.get(t);let a;return l?a=l:!o.length&&!n&&!r?a=t:(a={},o.length&&o.forEach(c=>kr(a,c,i,!0)),kr(a,t,i)),we(t)&&s.set(t,a),a}function kr(e,t,n,r=!1){const{mixins:o,extends:s}=t;s&&kr(e,s,n,!0),o&&o.forEach(i=>kr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const l=$c[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const $c={data:Ss,props:xs,emits:xs,methods:kn,computed:kn,beforeCreate:Ue,created:Ue,beforeMount:Ue,mounted:Ue,beforeUpdate:Ue,updated:Ue,beforeDestroy:Ue,beforeUnmount:Ue,destroyed:Ue,unmounted:Ue,activated:Ue,deactivated:Ue,errorCaptured:Ue,serverPrefetch:Ue,components:kn,directives:kn,watch:Mc,provide:Ss,inject:Nc};function Ss(e,t){return t?e?function(){return Pe(le(e)?e.call(this,this):e,le(t)?t.call(this,this):t)}:t:e}function Nc(e,t){return kn(Eo(e),Eo(t))}function Eo(e){if(ee(e)){const t={};for(let n=0;n1)return n&&le(t)?t.call(r&&r.proxy):t}}const cl={};function Bc(e,t,n,r=!1){const o={},s=Object.create(cl);e.propsDefaults=Object.create(null),ul(e,t,o,s);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:Ui(o):e.type.props?e.props=o:e.props=s,e.attrs=s}function Dc(e,t,n,r){const{props:o,attrs:s,vnode:{patchFlag:i}}=e,l=he(o),[a]=e.propsOptions;let c=!1;if((r||i>0)&&!(i&16)){if(i&8){const u=e.vnode.dynamicProps;for(let f=0;f{a=!0;const[d,m]=fl(f,t,!0);Pe(i,d),m&&l.push(...m)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!s&&!a)return we(e)&&r.set(e,un),un;if(ee(s))for(let u=0;u-1,m[1]=b<0||g-1||de(m,"default"))&&l.push(f)}}}const c=[i,l];return we(e)&&r.set(e,c),c}function Ls(e){return e[0]!=="$"&&!dn(e)}function ks(e){return e===null?"null":typeof e=="function"?e.name||"":typeof e=="object"&&e.constructor&&e.constructor.name||""}function As(e,t){return ks(e)===ks(t)}function Ts(e,t){return ee(t)?t.findIndex(n=>As(n,e)):le(t)&&As(t,e)?0:-1}const dl=e=>e[0]==="_"||e==="$stable",Ko=e=>ee(e)?e.map(ot):[ot(e)],zc=(e,t,n)=>{if(t._n)return t;const r=Le((...o)=>Ko(t(...o)),n);return r._c=!1,r},hl=(e,t,n)=>{const r=e._ctx;for(const o in e){if(dl(o))continue;const s=e[o];if(le(s))t[o]=zc(o,s,r);else if(s!=null){const i=Ko(s);t[o]=()=>i}}},pl=(e,t)=>{const n=Ko(t);e.slots.default=()=>n},jc=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=he(t),ki(e.slots,"_",n)):hl(t,e.slots={})}else e.slots={},t&&pl(e,t)},Uc=(e,t,n)=>{const{vnode:r,slots:o}=e;let s=!0,i=be;if(r.shapeFlag&32){const l=t._;l?n&&l===1?s=!1:(Pe(o,t),!n&&l===1&&delete o._):(s=!t.$stable,hl(t,o)),i=t}else t&&(pl(e,t),i={default:1});if(s)for(const l in o)!dl(l)&&i[l]==null&&delete o[l]};function Ar(e,t,n,r,o=!1){if(ee(e)){e.forEach((d,m)=>Ar(d,t&&(ee(t)?t[m]:t),n,r,o));return}if(pn(r)&&!o)return;const s=r.shapeFlag&4?Wr(r.component)||r.component.proxy:r.el,i=o?null:s,{i:l,r:a}=e,c=t&&t.r,u=l.refs===be?l.refs={}:l.refs,f=l.setupState;if(c!=null&&c!==a&&(Ae(c)?(u[c]=null,de(f,c)&&(f[c]=null)):je(c)&&(c.value=null)),le(a))Nt(a,l,12,[i,u]);else{const d=Ae(a),m=je(a);if(d||m){const g=()=>{if(e.f){const b=d?de(f,a)?f[a]:u[a]:a.value;o?ee(b)&&Io(b,s):ee(b)?b.includes(s)||b.push(s):d?(u[a]=[s],de(f,a)&&(f[a]=u[a])):(a.value=[s],e.k&&(u[e.k]=a.value))}else d?(u[a]=i,de(f,a)&&(f[a]=i)):m&&(a.value=i,e.k&&(u[e.k]=i))};i?(g.id=-1,Ve(g,n)):g()}}}let Lt=!1;const Wc=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",Vc=e=>e.namespaceURI.includes("MathML"),dr=e=>{if(Wc(e))return"svg";if(Vc(e))return"mathml"},hr=e=>e.nodeType===8;function Kc(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:s,parentNode:i,remove:l,insert:a,createComment:c}}=e,u=(v,C)=>{if(!C.hasChildNodes()){n(null,v,C),Sr(),C._vnode=v;return}Lt=!1,f(C.firstChild,v,null,null,null),Sr(),C._vnode=v,Lt&&console.error("Hydration completed but contains mismatches.")},f=(v,C,B,O,H,y=!1)=>{y=y||!!C.dynamicChildren;const G=hr(v)&&v.data==="[",L=()=>b(v,C,B,O,H,G),{type:W,ref:w,shapeFlag:M,patchFlag:te}=C;let ie=v.nodeType;C.el=v,te===-2&&(y=!1,C.dynamicChildren=null);let R=null;switch(W){case gn:ie!==3?C.children===""?(a(C.el=o(""),i(v),v),R=v):R=L():(v.data!==C.children&&(Lt=!0,v.data=C.children),R=s(v));break;case Je:T(v)?(R=s(v),A(C.el=v.content.firstChild,v,B)):ie!==8||G?R=L():R=s(v);break;case In:if(G&&(v=s(v),ie=v.nodeType),ie===1||ie===3){R=v;const Y=!C.children.length;for(let V=0;V{y=y||!!C.dynamicChildren;const{type:G,props:L,patchFlag:W,shapeFlag:w,dirs:M,transition:te}=C,ie=G==="input"||G==="option";if(ie||W!==-1){M&&dt(C,null,B,"created");let R=!1;if(T(v)){R=ml(O,te)&&B&&B.vnode.props&&B.vnode.props.appear;const V=v.content.firstChild;R&&te.beforeEnter(V),A(V,v,B),C.el=v=V}if(w&16&&!(L&&(L.innerHTML||L.textContent))){let V=m(v.firstChild,C,v,B,O,H,y);for(;V;){Lt=!0;const Re=V;V=V.nextSibling,l(Re)}}else w&8&&v.textContent!==C.children&&(Lt=!0,v.textContent=C.children);if(L)if(ie||!y||W&48)for(const V in L)(ie&&(V.endsWith("value")||V==="indeterminate")||Gn(V)&&!dn(V)||V[0]===".")&&r(v,V,null,L[V],void 0,void 0,B);else L.onClick&&r(v,"onClick",null,L.onClick,void 0,void 0,B);let Y;(Y=L&&L.onVnodeBeforeMount)&&Qe(Y,B,C),M&&dt(C,null,B,"beforeMount"),((Y=L&&L.onVnodeMounted)||M||R)&&el(()=>{Y&&Qe(Y,B,C),R&&te.enter(v),M&&dt(C,null,B,"mounted")},O)}return v.nextSibling},m=(v,C,B,O,H,y,G)=>{G=G||!!C.dynamicChildren;const L=C.children,W=L.length;for(let w=0;w{const{slotScopeIds:G}=C;G&&(H=H?H.concat(G):G);const L=i(v),W=m(s(v),C,L,B,O,H,y);return W&&hr(W)&&W.data==="]"?s(C.anchor=W):(Lt=!0,a(C.anchor=c("]"),L,W),W)},b=(v,C,B,O,H,y)=>{if(Lt=!0,C.el=null,y){const W=E(v);for(;;){const w=s(v);if(w&&w!==W)l(w);else break}}const G=s(v),L=i(v);return l(v),n(null,C,L,G,B,O,dr(L),H),G},E=(v,C="[",B="]")=>{let O=0;for(;v;)if(v=s(v),v&&hr(v)&&(v.data===C&&O++,v.data===B)){if(O===0)return s(v);O--}return v},A=(v,C,B)=>{const O=C.parentNode;O&&O.replaceChild(v,C);let H=B;for(;H;)H.vnode.el===C&&(H.vnode.el=H.subTree.el=v),H=H.parent},T=v=>v.nodeType===1&&v.tagName.toLowerCase()==="template";return[u,f]}const Ve=el;function qc(e){return Gc(e,Kc)}function Gc(e,t){const n=Ai();n.__VUE__=!0;const{insert:r,remove:o,patchProp:s,createElement:i,createText:l,createComment:a,setText:c,setElementText:u,parentNode:f,nextSibling:d,setScopeId:m=Ze,insertStaticContent:g}=e,b=(h,p,_,k=null,S=null,$=null,z=void 0,N=null,F=!!p.dynamicChildren)=>{if(h===p)return;h&&!Jt(h,p)&&(k=x(h),Be(h,S,$,!0),h=null),p.patchFlag===-2&&(F=!1,p.dynamicChildren=null);const{type:P,ref:K,shapeFlag:Q}=p;switch(P){case gn:E(h,p,_,k);break;case Je:A(h,p,_,k);break;case In:h==null&&T(p,_,k,z);break;case _e:w(h,p,_,k,S,$,z,N,F);break;default:Q&1?B(h,p,_,k,S,$,z,N,F):Q&6?M(h,p,_,k,S,$,z,N,F):(Q&64||Q&128)&&P.process(h,p,_,k,S,$,z,N,F,q)}K!=null&&S&&Ar(K,h&&h.ref,$,p||h,!p)},E=(h,p,_,k)=>{if(h==null)r(p.el=l(p.children),_,k);else{const S=p.el=h.el;p.children!==h.children&&c(S,p.children)}},A=(h,p,_,k)=>{h==null?r(p.el=a(p.children||""),_,k):p.el=h.el},T=(h,p,_,k)=>{[h.el,h.anchor]=g(h.children,p,_,k,h.el,h.anchor)},v=({el:h,anchor:p},_,k)=>{let S;for(;h&&h!==p;)S=d(h),r(h,_,k),h=S;r(p,_,k)},C=({el:h,anchor:p})=>{let _;for(;h&&h!==p;)_=d(h),o(h),h=_;o(p)},B=(h,p,_,k,S,$,z,N,F)=>{p.type==="svg"?z="svg":p.type==="math"&&(z="mathml"),h==null?O(p,_,k,S,$,z,N,F):G(h,p,S,$,z,N,F)},O=(h,p,_,k,S,$,z,N)=>{let F,P;const{props:K,shapeFlag:Q,transition:J,dirs:re}=h;if(F=h.el=i(h.type,$,K&&K.is,K),Q&8?u(F,h.children):Q&16&&y(h.children,F,null,k,S,no(h,$),z,N),re&&dt(h,null,k,"created"),H(F,h,h.scopeId,z,k),K){for(const ve in K)ve!=="value"&&!dn(ve)&&s(F,ve,null,K[ve],$,h.children,k,S,Oe);"value"in K&&s(F,"value",null,K.value,$),(P=K.onVnodeBeforeMount)&&Qe(P,k,h)}re&&dt(h,null,k,"beforeMount");const ae=ml(S,J);ae&&J.beforeEnter(F),r(F,p,_),((P=K&&K.onVnodeMounted)||ae||re)&&Ve(()=>{P&&Qe(P,k,h),ae&&J.enter(F),re&&dt(h,null,k,"mounted")},S)},H=(h,p,_,k,S)=>{if(_&&m(h,_),k)for(let $=0;${for(let P=F;P{const N=p.el=h.el;let{patchFlag:F,dynamicChildren:P,dirs:K}=p;F|=h.patchFlag&16;const Q=h.props||be,J=p.props||be;let re;if(_&&Wt(_,!1),(re=J.onVnodeBeforeUpdate)&&Qe(re,_,p,h),K&&dt(p,h,_,"beforeUpdate"),_&&Wt(_,!0),P?L(h.dynamicChildren,P,N,_,k,no(p,S),$):z||V(h,p,N,null,_,k,no(p,S),$,!1),F>0){if(F&16)W(N,p,Q,J,_,k,S);else if(F&2&&Q.class!==J.class&&s(N,"class",null,J.class,S),F&4&&s(N,"style",Q.style,J.style,S),F&8){const ae=p.dynamicProps;for(let ve=0;ve{re&&Qe(re,_,p,h),K&&dt(p,h,_,"updated")},k)},L=(h,p,_,k,S,$,z)=>{for(let N=0;N{if(_!==k){if(_!==be)for(const N in _)!dn(N)&&!(N in k)&&s(h,N,_[N],null,z,p.children,S,$,Oe);for(const N in k){if(dn(N))continue;const F=k[N],P=_[N];F!==P&&N!=="value"&&s(h,N,P,F,z,p.children,S,$,Oe)}"value"in k&&s(h,"value",_.value,k.value,z)}},w=(h,p,_,k,S,$,z,N,F)=>{const P=p.el=h?h.el:l(""),K=p.anchor=h?h.anchor:l("");let{patchFlag:Q,dynamicChildren:J,slotScopeIds:re}=p;re&&(N=N?N.concat(re):re),h==null?(r(P,_,k),r(K,_,k),y(p.children||[],_,K,S,$,z,N,F)):Q>0&&Q&64&&J&&h.dynamicChildren?(L(h.dynamicChildren,J,_,S,$,z,N),(p.key!=null||S&&p===S.subTree)&&gl(h,p,!0)):V(h,p,_,K,S,$,z,N,F)},M=(h,p,_,k,S,$,z,N,F)=>{p.slotScopeIds=N,h==null?p.shapeFlag&512?S.ctx.activate(p,_,k,z,F):te(p,_,k,S,$,z,F):ie(h,p,F)},te=(h,p,_,k,S,$,z)=>{const N=h.component=ru(h,k,S);if(Zn(h)&&(N.ctx.renderer=q),ou(N),N.asyncDep){if(S&&S.registerDep(N,R),!h.el){const F=N.subTree=se(Je);A(null,F,p,_)}}else R(N,h,p,_,S,$,z)},ie=(h,p,_)=>{const k=p.component=h.component;if(ac(h,p,_))if(k.asyncDep&&!k.asyncResolved){Y(k,p,_);return}else k.next=p,nc(k.update),k.effect.dirty=!0,k.update();else p.el=h.el,k.vnode=p},R=(h,p,_,k,S,$,z)=>{const N=()=>{if(h.isMounted){let{next:K,bu:Q,u:J,parent:re,vnode:ae}=h;{const ln=vl(h);if(ln){K&&(K.el=ae.el,Y(h,K,z)),ln.asyncDep.then(()=>{h.isUnmounted||N()});return}}let ve=K,Ee;Wt(h,!1),K?(K.el=ae.el,Y(h,K,z)):K=ae,Q&&Jr(Q),(Ee=K.props&&K.props.onVnodeBeforeUpdate)&&Qe(Ee,re,K,ae),Wt(h,!0);const Ie=Qr(h),nt=h.subTree;h.subTree=Ie,b(nt,Ie,f(nt.el),x(nt),h,S,$),K.el=Ie.el,ve===null&&cc(h,Ie.el),J&&Ve(J,S),(Ee=K.props&&K.props.onVnodeUpdated)&&Ve(()=>Qe(Ee,re,K,ae),S)}else{let K;const{el:Q,props:J}=p,{bm:re,m:ae,parent:ve}=h,Ee=pn(p);if(Wt(h,!1),re&&Jr(re),!Ee&&(K=J&&J.onVnodeBeforeMount)&&Qe(K,ve,p),Wt(h,!0),Q&&ge){const Ie=()=>{h.subTree=Qr(h),ge(Q,h.subTree,h,S,null)};Ee?p.type.__asyncLoader().then(()=>!h.isUnmounted&&Ie()):Ie()}else{const Ie=h.subTree=Qr(h);b(null,Ie,_,k,h,S,$),p.el=Ie.el}if(ae&&Ve(ae,S),!Ee&&(K=J&&J.onVnodeMounted)){const Ie=p;Ve(()=>Qe(K,ve,Ie),S)}(p.shapeFlag&256||ve&&pn(ve.vnode)&&ve.vnode.shapeFlag&256)&&h.a&&Ve(h.a,S),h.isMounted=!0,p=_=k=null}},F=h.effect=new No(N,Ze,()=>Fr(P),h.scope),P=h.update=()=>{F.dirty&&F.run()};P.id=h.uid,Wt(h,!0),P()},Y=(h,p,_)=>{p.component=h;const k=h.vnode.props;h.vnode=p,h.next=null,Dc(h,p.props,k,_),Uc(h,p.children,_),zt(),ys(h),jt()},V=(h,p,_,k,S,$,z,N,F=!1)=>{const P=h&&h.children,K=h?h.shapeFlag:0,Q=p.children,{patchFlag:J,shapeFlag:re}=p;if(J>0){if(J&128){He(P,Q,_,k,S,$,z,N,F);return}else if(J&256){Re(P,Q,_,k,S,$,z,N,F);return}}re&8?(K&16&&Oe(P,S,$),Q!==P&&u(_,Q)):K&16?re&16?He(P,Q,_,k,S,$,z,N,F):Oe(P,S,$,!0):(K&8&&u(_,""),re&16&&y(Q,_,k,S,$,z,N,F))},Re=(h,p,_,k,S,$,z,N,F)=>{h=h||un,p=p||un;const P=h.length,K=p.length,Q=Math.min(P,K);let J;for(J=0;JK?Oe(h,S,$,!0,!1,Q):y(p,_,k,S,$,z,N,F,Q)},He=(h,p,_,k,S,$,z,N,F)=>{let P=0;const K=p.length;let Q=h.length-1,J=K-1;for(;P<=Q&&P<=J;){const re=h[P],ae=p[P]=F?Rt(p[P]):ot(p[P]);if(Jt(re,ae))b(re,ae,_,null,S,$,z,N,F);else break;P++}for(;P<=Q&&P<=J;){const re=h[Q],ae=p[J]=F?Rt(p[J]):ot(p[J]);if(Jt(re,ae))b(re,ae,_,null,S,$,z,N,F);else break;Q--,J--}if(P>Q){if(P<=J){const re=J+1,ae=reJ)for(;P<=Q;)Be(h[P],S,$,!0),P++;else{const re=P,ae=P,ve=new Map;for(P=ae;P<=J;P++){const Ge=p[P]=F?Rt(p[P]):ot(p[P]);Ge.key!=null&&ve.set(Ge.key,P)}let Ee,Ie=0;const nt=J-ae+1;let ln=!1,ls=0;const En=new Array(nt);for(P=0;P=nt){Be(Ge,S,$,!0);continue}let ft;if(Ge.key!=null)ft=ve.get(Ge.key);else for(Ee=ae;Ee<=J;Ee++)if(En[Ee-ae]===0&&Jt(Ge,p[Ee])){ft=Ee;break}ft===void 0?Be(Ge,S,$,!0):(En[ft-ae]=P+1,ft>=ls?ls=ft:ln=!0,b(Ge,p[ft],_,null,S,$,z,N,F),Ie++)}const as=ln?Yc(En):un;for(Ee=as.length-1,P=nt-1;P>=0;P--){const Ge=ae+P,ft=p[Ge],cs=Ge+1{const{el:$,type:z,transition:N,children:F,shapeFlag:P}=h;if(P&6){We(h.component.subTree,p,_,k);return}if(P&128){h.suspense.move(p,_,k);return}if(P&64){z.move(h,p,_,q);return}if(z===_e){r($,p,_);for(let Q=0;QN.enter($),S);else{const{leave:Q,delayLeave:J,afterLeave:re}=N,ae=()=>r($,p,_),ve=()=>{Q($,()=>{ae(),re&&re()})};J?J($,ae,ve):ve()}else r($,p,_)},Be=(h,p,_,k=!1,S=!1)=>{const{type:$,props:z,ref:N,children:F,dynamicChildren:P,shapeFlag:K,patchFlag:Q,dirs:J}=h;if(N!=null&&Ar(N,null,_,h,!0),K&256){p.ctx.deactivate(h);return}const re=K&1&&J,ae=!pn(h);let ve;if(ae&&(ve=z&&z.onVnodeBeforeUnmount)&&Qe(ve,p,h),K&6)ut(h.component,_,k);else{if(K&128){h.suspense.unmount(_,k);return}re&&dt(h,null,p,"beforeUnmount"),K&64?h.type.remove(h,p,_,S,q,k):P&&($!==_e||Q>0&&Q&64)?Oe(P,p,_,!1,!0):($===_e&&Q&384||!S&&K&16)&&Oe(F,p,_),k&&Ct(h)}(ae&&(ve=z&&z.onVnodeUnmounted)||re)&&Ve(()=>{ve&&Qe(ve,p,h),re&&dt(h,null,p,"unmounted")},_)},Ct=h=>{const{type:p,el:_,anchor:k,transition:S}=h;if(p===_e){St(_,k);return}if(p===In){C(h);return}const $=()=>{o(_),S&&!S.persisted&&S.afterLeave&&S.afterLeave()};if(h.shapeFlag&1&&S&&!S.persisted){const{leave:z,delayLeave:N}=S,F=()=>z(_,$);N?N(h.el,$,F):F()}else $()},St=(h,p)=>{let _;for(;h!==p;)_=d(h),o(h),h=_;o(p)},ut=(h,p,_)=>{const{bum:k,scope:S,update:$,subTree:z,um:N}=h;k&&Jr(k),S.stop(),$&&($.active=!1,Be(z,h,p,_)),N&&Ve(N,p),Ve(()=>{h.isUnmounted=!0},p),p&&p.pendingBranch&&!p.isUnmounted&&h.asyncDep&&!h.asyncResolved&&h.suspenseId===p.pendingId&&(p.deps--,p.deps===0&&p.resolve())},Oe=(h,p,_,k=!1,S=!1,$=0)=>{for(let z=$;zh.shapeFlag&6?x(h.component.subTree):h.shapeFlag&128?h.suspense.next():d(h.anchor||h.el);let U=!1;const D=(h,p,_)=>{h==null?p._vnode&&Be(p._vnode,null,null,!0):b(p._vnode||null,h,p,null,null,null,_),U||(U=!0,ys(),Sr(),U=!1),p._vnode=h},q={p:b,um:Be,m:We,r:Ct,mt:te,mc:y,pc:V,pbc:L,n:x,o:e};let ue,ge;return t&&([ue,ge]=t(q)),{render:D,hydrate:ue,createApp:Fc(D,ue)}}function no({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function Wt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function ml(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function gl(e,t,n=!1){const r=e.children,o=t.children;if(ee(r)&&ee(o))for(let s=0;s>1,e[n[l]]0&&(t[r]=n[s-1]),n[s]=r)}}for(s=n.length,i=n[s-1];s-- >0;)n[s]=i,i=t[i];return n}function vl(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:vl(t)}const Jc=e=>e.__isTeleport,_e=Symbol.for("v-fgt"),gn=Symbol.for("v-txt"),Je=Symbol.for("v-cmt"),In=Symbol.for("v-stc"),$n=[];let st=null;function j(e=!1){$n.push(st=e?null:[])}function Xc(){$n.pop(),st=$n[$n.length-1]||null}let zn=1;function Ps(e){zn+=e}function yl(e){return e.dynamicChildren=zn>0?st||un:null,Xc(),zn>0&&st&&st.push(e),e}function Z(e,t,n,r,o,s){return yl(ne(e,t,n,r,o,s,!0))}function xe(e,t,n,r,o){return yl(se(e,t,n,r,o,!0))}function Tr(e){return e?e.__v_isVNode===!0:!1}function Jt(e,t){return e.type===t.type&&e.key===t.key}const _l=({key:e})=>e??null,_r=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Ae(e)||je(e)||le(e)?{i:Te,r:e,k:t,f:!!n}:e:null);function ne(e,t=null,n=null,r=0,o=null,s=e===_e?0:1,i=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&_l(t),ref:t&&_r(t),scopeId:Qi,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:s,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:Te};return l?(qo(a,n),s&128&&e.normalize(a)):n&&(a.shapeFlag|=Ae(n)?8:16),zn>0&&!i&&st&&(a.patchFlag>0||s&6)&&a.patchFlag!==32&&st.push(a),a}const se=Qc;function Qc(e,t=null,n=null,r=0,o=null,s=!1){if((!e||e===uc)&&(e=Je),Tr(e)){const l=Bt(e,t,!0);return n&&qo(l,n),zn>0&&!s&&st&&(l.shapeFlag&6?st[st.indexOf(e)]=l:st.push(l)),l.patchFlag|=-2,l}if(cu(e)&&(e=e.__vccOpts),t){t=Zc(t);let{class:l,style:a}=t;l&&!Ae(l)&&(t.class=Ke(l)),we(a)&&(Wi(a)&&!ee(a)&&(a=Pe({},a)),t.style=Jn(a))}const i=Ae(e)?1:dc(e)?128:Jc(e)?64:we(e)?4:le(e)?2:0;return ne(e,t,n,r,o,i,s,!0)}function Zc(e){return e?Wi(e)||Object.getPrototypeOf(e)===cl?Pe({},e):e:null}function Bt(e,t,n=!1){const{props:r,ref:o,patchFlag:s,children:i}=e,l=t?So(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&_l(l),ref:t&&t.ref?n&&o?ee(o)?o.concat(_r(t)):[o,_r(t)]:_r(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==_e?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Bt(e.ssContent),ssFallback:e.ssFallback&&Bt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function mt(e=" ",t=0){return se(gn,null,e,t)}function eu(e,t){const n=se(In,null,e);return n.staticCount=t,n}function ke(e="",t=!1){return t?(j(),xe(Je,null,e)):se(Je,null,e)}function ot(e){return e==null||typeof e=="boolean"?se(Je):ee(e)?se(_e,null,e.slice()):typeof e=="object"?Rt(e):se(gn,null,String(e))}function Rt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Bt(e)}function qo(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ee(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),qo(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;o?o===3&&Te&&(Te.slots._===1?t._=1:(t._=2,e.patchFlag|=1024)):t._ctx=Te}else le(t)?(t={default:t,_ctx:Te},n=32):(t=String(t),r&64?(n=16,t=[mt(t)]):n=8);e.children=t,e.shapeFlag|=n}function So(...e){const t={};for(let n=0;n$e||Te;let Pr,xo;{const e=Ai(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),s=>{o.length>1?o.forEach(i=>i(s)):o[0](s)}};Pr=t("__VUE_INSTANCE_SETTERS__",n=>$e=n),xo=t("__VUE_SSR_SETTERS__",n=>tr=n)}const er=e=>{const t=$e;return Pr(e),e.scope.on(),()=>{e.scope.off(),Pr(t)}},Rs=()=>{$e&&$e.scope.off(),Pr(null)};function bl(e){return e.vnode.shapeFlag&4}let tr=!1;function ou(e,t=!1){t&&xo(t);const{props:n,children:r}=e.vnode,o=bl(e);Bc(e,n,o,t),jc(e,r);const s=o?su(e,t):void 0;return t&&xo(!1),s}function su(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Rc);const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?lu(e):null,s=er(e);zt();const i=Nt(r,e,0,[e.props,o]);if(jt(),s(),Si(i)){if(i.then(Rs,Rs),t)return i.then(l=>{Os(e,l,t)}).catch(l=>{Qn(l,e,0)});e.asyncDep=i}else Os(e,i,t)}else wl(e,t)}function Os(e,t,n){le(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:we(t)&&(e.setupState=qi(t)),wl(e,n)}let Is;function wl(e,t,n){const r=e.type;if(!e.render){if(!t&&Is&&!r.render){const o=r.template||Vo(e).template;if(o){const{isCustomElement:s,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:a}=r,c=Pe(Pe({isCustomElement:s,delimiters:l},i),a);r.render=Is(o,c)}}e.render=r.render||Ze}{const o=er(e);zt();try{Oc(e)}finally{jt(),o()}}}const iu={get(e,t){return qe(e,"get",""),e[t]}};function lu(e){const t=n=>{e.exposed=n||{}};return{attrs:new Proxy(e.attrs,iu),slots:e.slots,emit:e.emit,expose:t}}function Wr(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(qi(Ka(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Rn)return Rn[n](e)},has(t,n){return n in t||n in Rn}}))}function au(e,t=!0){return le(e)?e.displayName||e.name:e.name||t&&e.__name}function cu(e){return le(e)&&"__vccOpts"in e}const I=(e,t)=>qa(e,t,tr);function oe(e,t,n){const r=arguments.length;return r===2?we(t)&&!ee(t)?Tr(t)?se(e,null,[t]):se(e,t):se(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Tr(n)&&(n=[n]),se(e,t,n))}const uu="3.4.22";/** +* @vue/runtime-dom v3.4.22 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const fu="http://www.w3.org/2000/svg",du="http://www.w3.org/1998/Math/MathML",Ot=typeof document<"u"?document:null,$s=Ot&&Ot.createElement("template"),hu={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t==="svg"?Ot.createElementNS(fu,e):t==="mathml"?Ot.createElementNS(du,e):Ot.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>Ot.createTextNode(e),createComment:e=>Ot.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Ot.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,s){const i=n?n.previousSibling:t.lastChild;if(o&&(o===s||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===s||!(o=o.nextSibling)););else{$s.innerHTML=r==="svg"?`${e}`:r==="mathml"?`${e}`:e;const l=$s.content;if(r==="svg"||r==="mathml"){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},kt="transition",Cn="animation",jn=Symbol("_vtc"),bn=(e,{slots:t})=>oe(_c,pu(e),t);bn.displayName="Transition";const El={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};bn.props=Pe({},nl,El);const Vt=(e,t=[])=>{ee(e)?e.forEach(n=>n(...t)):e&&e(...t)},Ns=e=>e?ee(e)?e.some(t=>t.length>1):e.length>1:!1;function pu(e){const t={};for(const w in e)w in El||(t[w]=e[w]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:s=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=s,appearActiveClass:c=i,appearToClass:u=l,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:d=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,g=mu(o),b=g&&g[0],E=g&&g[1],{onBeforeEnter:A,onEnter:T,onEnterCancelled:v,onLeave:C,onLeaveCancelled:B,onBeforeAppear:O=A,onAppear:H=T,onAppearCancelled:y=v}=t,G=(w,M,te)=>{Kt(w,M?u:l),Kt(w,M?c:i),te&&te()},L=(w,M)=>{w._isLeaving=!1,Kt(w,f),Kt(w,m),Kt(w,d),M&&M()},W=w=>(M,te)=>{const ie=w?H:T,R=()=>G(M,w,te);Vt(ie,[M,R]),Ms(()=>{Kt(M,w?a:s),At(M,w?u:l),Ns(ie)||Hs(M,r,b,R)})};return Pe(t,{onBeforeEnter(w){Vt(A,[w]),At(w,s),At(w,i)},onBeforeAppear(w){Vt(O,[w]),At(w,a),At(w,c)},onEnter:W(!1),onAppear:W(!0),onLeave(w,M){w._isLeaving=!0;const te=()=>L(w,M);At(w,f),yu(),At(w,d),Ms(()=>{w._isLeaving&&(Kt(w,f),At(w,m),Ns(C)||Hs(w,r,E,te))}),Vt(C,[w,te])},onEnterCancelled(w){G(w,!1),Vt(v,[w])},onAppearCancelled(w){G(w,!0),Vt(y,[w])},onLeaveCancelled(w){L(w),Vt(B,[w])}})}function mu(e){if(e==null)return null;if(we(e))return[ro(e.enter),ro(e.leave)];{const t=ro(e);return[t,t]}}function ro(e){return ga(e)}function At(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[jn]||(e[jn]=new Set)).add(t)}function Kt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[jn];n&&(n.delete(t),n.size||(e[jn]=void 0))}function Ms(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let gu=0;function Hs(e,t,n,r){const o=e._endId=++gu,s=()=>{o===e._endId&&r()};if(n)return setTimeout(s,n);const{type:i,timeout:l,propCount:a}=vu(e,t);if(!i)return r();const c=i+"end";let u=0;const f=()=>{e.removeEventListener(c,d),s()},d=m=>{m.target===e&&++u>=a&&f()};setTimeout(()=>{u(n[g]||"").split(", "),o=r(`${kt}Delay`),s=r(`${kt}Duration`),i=Fs(o,s),l=r(`${Cn}Delay`),a=r(`${Cn}Duration`),c=Fs(l,a);let u=null,f=0,d=0;t===kt?i>0&&(u=kt,f=i,d=s.length):t===Cn?c>0&&(u=Cn,f=c,d=a.length):(f=Math.max(i,c),u=f>0?i>c?kt:Cn:null,d=u?u===kt?s.length:a.length:0);const m=u===kt&&/\b(transform|all)(,|$)/.test(r(`${kt}Property`).toString());return{type:u,timeout:f,propCount:d,hasTransform:m}}function Fs(e,t){for(;e.lengthBs(n)+Bs(e[r])))}function Bs(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function yu(){return document.body.offsetHeight}function _u(e,t,n){const r=e[jn];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Rr=Symbol("_vod"),Cl=Symbol("_vsh"),Or={beforeMount(e,{value:t},{transition:n}){e[Rr]=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):Sn(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Sn(e,!0),r.enter(e)):r.leave(e,()=>{Sn(e,!1)}):Sn(e,t))},beforeUnmount(e,{value:t}){Sn(e,t)}};function Sn(e,t){e.style.display=t?e[Rr]:"none",e[Cl]=!t}const bu=Symbol(""),wu=/(^|;)\s*display\s*:/;function Eu(e,t,n){const r=e.style,o=Ae(n);let s=!1;if(n&&!o){if(t)if(Ae(t))for(const i of t.split(";")){const l=i.slice(0,i.indexOf(":")).trim();n[l]==null&&br(r,l,"")}else for(const i in t)n[i]==null&&br(r,i,"");for(const i in n)i==="display"&&(s=!0),br(r,i,n[i])}else if(o){if(t!==n){const i=r[bu];i&&(n+=";"+i),r.cssText=n,s=wu.test(n)}}else t&&e.removeAttribute("style");Rr in e&&(e[Rr]=s?r.display:"",e[Cl]&&(r.display="none"))}const Ds=/\s*!important$/;function br(e,t,n){if(ee(n))n.forEach(r=>br(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Cu(e,t);Ds.test(n)?e.setProperty(rn(r),n.replace(Ds,""),"important"):e[r]=n}}const zs=["Webkit","Moz","ms"],oo={};function Cu(e,t){const n=oo[t];if(n)return n;let r=tt(t);if(r!=="filter"&&r in e)return oo[t]=r;r=Yn(r);for(let o=0;oso||(Pu.then(()=>so=0),so=Date.now());function Ou(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;et(Iu(r,n.value),t,5,[r])};return n.value=e,n.attached=Ru(),n}function Iu(e,t){if(ee(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const Vs=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,$u=(e,t,n,r,o,s,i,l,a)=>{const c=o==="svg";t==="class"?_u(e,r,c):t==="style"?Eu(e,n,r):Gn(t)?Oo(t)||Au(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Nu(e,t,r,c))?xu(e,t,r,s,i,l,a):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Su(e,t,r,c))};function Nu(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&Vs(t)&&le(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const o=e.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return Vs(t)&&Ae(n)?!1:t in e}const Mu={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},Hu=(e,t)=>{const n=e._withKeys||(e._withKeys={}),r=t.join(".");return n[r]||(n[r]=o=>{if(!("key"in o))return;const s=rn(o.key);if(t.some(i=>i===s||Mu[i]===s))return e(o)})},Fu=Pe({patchProp:$u},hu);let io,Ks=!1;function Bu(){return io=Ks?io:qc(Fu),Ks=!0,io}const Du=(...e)=>{const t=Bu().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=ju(r);if(o)return n(o,!0,zu(o))},t};function zu(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function ju(e){return Ae(e)?document.querySelector(e):e}var Uu=["link","meta","script","style","noscript","template"],Wu=["title","base"],Vu=([e,t,n])=>Wu.includes(e)?e:Uu.includes(e)?e==="meta"&&t.name?`${e}.${t.name}`:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,Object.entries(t).map(([r,o])=>typeof o=="boolean"?o?[r,""]:null:[r,o]).filter(r=>r!=null).sort(([r],[o])=>r.localeCompare(o)),n]):null,Ku=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=Vu(r);o&&!t.has(o)&&(t.add(o),n.push(r))}),n},nr=e=>/^(https?:)?\/\//.test(e),Sl=e=>/^[a-z][a-z0-9+.-]*:/.test(e),Go=e=>Object.prototype.toString.call(e)==="[object Object]",qu=e=>{const[t,...n]=e.split(/(\?|#)/);if(!t||t.endsWith("/"))return e;let r=t.replace(/(^|\/)README.md$/i,"$1index.html");return r.endsWith(".md")?r=r.substring(0,r.length-3)+".html":r.endsWith(".html")||(r=r+".html"),r.endsWith("/index.html")&&(r=r.substring(0,r.length-10)),r+n.join("")},xl=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Ll=e=>e[0]==="/"?e.slice(1):e,kl=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const s=o.split("/").length-r.split("/").length;return s!==0?s:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"},it=e=>typeof e=="string";const Gu="modulepreload",Yu=function(e){return"/ios-training/"+e},qs={},rt=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){const s=document.getElementsByTagName("link"),i=document.querySelector("meta[property=csp-nonce]"),l=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));o=Promise.all(n.map(a=>{if(a=Yu(a),a in qs)return;qs[a]=!0;const c=a.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!r)for(let m=s.length-1;m>=0;m--){const g=s[m];if(g.href===a&&(!c||g.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${u}`))return;const d=document.createElement("link");if(d.rel=c?"stylesheet":Gu,c||(d.as="script",d.crossOrigin=""),d.href=a,l&&d.setAttribute("nonce",l),document.head.appendChild(d),c)return new Promise((m,g)=>{d.addEventListener("load",m),d.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${a}`)))})}))}return o.then(()=>t()).catch(s=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s})},Ju=JSON.parse("{}"),Xu=Object.fromEntries([["/",{loader:()=>rt(()=>import("./index.html-C39pB5w2.js"),[]),meta:{title:"Welcome"}}],["/api-communication/",{loader:()=>rt(()=>import("./index.html-Cv2fRjYf.js"),[]),meta:{title:"Communicate with a REST API"}}],["/mini-project/",{loader:()=>rt(()=>import("./index.html-z4YYCP43.js"),[]),meta:{title:"Mini project"}}],["/persist-data/",{loader:()=>rt(()=>import("./index.html-ChAAQVq_.js"),[]),meta:{title:"Locally persisting data"}}],["/presentation/",{loader:()=>rt(()=>import("./index.html-DOlN3MeL.js"),[]),meta:{title:"Presentation"}}],["/swift-part1/",{loader:()=>rt(()=>import("./index.html-B8dggsdP.js"),[]),meta:{title:"Swift (part 1)"}}],["/swift-part2/",{loader:()=>rt(()=>import("./index.html-5n7MuywO.js"),[]),meta:{title:"Swift (part 2)"}}],["/to-go-further/",{loader:()=>rt(()=>import("./index.html-CfxHtkWF.js"),[]),meta:{title:"Going further"}}],["/ui-development/",{loader:()=>rt(()=>import("./index.html-DKuyFSlS.js"),[]),meta:{title:"UI development"}}],["/404.html",{loader:()=>rt(()=>import("./404.html-9pEcDQrh.js"),[]),meta:{title:""}}]]);/*! + * vue-router v4.3.0 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */const cn=typeof document<"u";function Qu(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const me=Object.assign;function lo(e,t){const n={};for(const r in t){const o=t[r];n[r]=lt(o)?o.map(e):e(o)}return n}const Nn=()=>{},lt=Array.isArray,Al=/#/g,Zu=/&/g,ef=/\//g,tf=/=/g,nf=/\?/g,Tl=/\+/g,rf=/%5B/g,of=/%5D/g,Pl=/%5E/g,sf=/%60/g,Rl=/%7B/g,lf=/%7C/g,Ol=/%7D/g,af=/%20/g;function Yo(e){return encodeURI(""+e).replace(lf,"|").replace(rf,"[").replace(of,"]")}function cf(e){return Yo(e).replace(Rl,"{").replace(Ol,"}").replace(Pl,"^")}function Lo(e){return Yo(e).replace(Tl,"%2B").replace(af,"+").replace(Al,"%23").replace(Zu,"%26").replace(sf,"`").replace(Rl,"{").replace(Ol,"}").replace(Pl,"^")}function uf(e){return Lo(e).replace(tf,"%3D")}function ff(e){return Yo(e).replace(Al,"%23").replace(nf,"%3F")}function df(e){return e==null?"":ff(e).replace(ef,"%2F")}function Un(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const hf=/\/$/,pf=e=>e.replace(hf,"");function ao(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(r=t.slice(0,a),s=t.slice(a+1,l>-1?l:t.length),o=e(s)),l>-1&&(r=r||t.slice(0,l),i=t.slice(l,t.length)),r=yf(r??t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:Un(i)}}function mf(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Gs(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function gf(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&vn(t.matched[r],n.matched[o])&&Il(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function vn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Il(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!vf(e[n],t[n]))return!1;return!0}function vf(e,t){return lt(e)?Ys(e,t):lt(t)?Ys(t,e):e===t}function Ys(e,t){return lt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function yf(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let s=n.length-1,i,l;for(i=0;i1&&s--;else break;return n.slice(0,s).join("/")+"/"+r.slice(i).join("/")}var Wn;(function(e){e.pop="pop",e.push="push"})(Wn||(Wn={}));var Mn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Mn||(Mn={}));function _f(e){if(!e)if(cn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),pf(e)}const bf=/^[^#]+#/;function wf(e,t){return e.replace(bf,"#")+t}function Ef(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const Vr=()=>({left:window.scrollX,top:window.scrollY});function Cf(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=Ef(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function Js(e,t){return(history.state?history.state.position-t:-1)+e}const ko=new Map;function Sf(e,t){ko.set(e,t)}function xf(e){const t=ko.get(e);return ko.delete(e),t}let Lf=()=>location.protocol+"//"+location.host;function $l(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let l=o.includes(e.slice(s))?e.slice(s).length:1,a=o.slice(l);return a[0]!=="/"&&(a="/"+a),Gs(a,"")}return Gs(n,e)+r+o}function kf(e,t,n,r){let o=[],s=[],i=null;const l=({state:d})=>{const m=$l(e,location),g=n.value,b=t.value;let E=0;if(d){if(n.value=m,t.value=d,i&&i===g){i=null;return}E=b?d.position-b.position:0}else r(m);o.forEach(A=>{A(n.value,g,{delta:E,type:Wn.pop,direction:E?E>0?Mn.forward:Mn.back:Mn.unknown})})};function a(){i=n.value}function c(d){o.push(d);const m=()=>{const g=o.indexOf(d);g>-1&&o.splice(g,1)};return s.push(m),m}function u(){const{history:d}=window;d.state&&d.replaceState(me({},d.state,{scroll:Vr()}),"")}function f(){for(const d of s)d();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:f}}function Xs(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?Vr():null}}function Af(e){const{history:t,location:n}=window,r={value:$l(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(a,c,u){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+a:Lf()+e+a;try{t[u?"replaceState":"pushState"](c,"",d),o.value=c}catch(m){console.error(m),n[u?"replace":"assign"](d)}}function i(a,c){const u=me({},t.state,Xs(o.value.back,a,o.value.forward,!0),c,{position:o.value.position});s(a,u,!0),r.value=a}function l(a,c){const u=me({},o.value,t.state,{forward:a,scroll:Vr()});s(u.current,u,!0);const f=me({},Xs(r.value,a,null),{position:u.position+1},c);s(a,f,!1),r.value=a}return{location:r,state:o,push:l,replace:i}}function Tf(e){e=_f(e);const t=Af(e),n=kf(e,t.state,t.location,t.replace);function r(s,i=!0){i||n.pauseListeners(),history.go(s)}const o=me({location:"",base:e,go:r,createHref:wf.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function Pf(e){return typeof e=="string"||e&&typeof e=="object"}function Nl(e){return typeof e=="string"||typeof e=="symbol"}const _t={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ml=Symbol("");var Qs;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Qs||(Qs={}));function yn(e,t){return me(new Error,{type:e,[Ml]:!0},t)}function yt(e,t){return e instanceof Error&&Ml in e&&(t==null||!!(e.type&t))}const Zs="[^/]+?",Rf={sensitive:!1,strict:!1,start:!0,end:!0},Of=/[.+*?^${}()[\]/\\]/g;function If(e,t){const n=me({},Rf,t),r=[];let o=n.start?"^":"";const s=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===80?1:-1:0}function Nf(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const Mf={type:0,value:""},Hf=/[a-zA-Z0-9_]/;function Ff(e){if(!e)return[[]];if(e==="/")return[[Mf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=0,r=n;const o=[];let s;function i(){s&&o.push(s),s=[]}let l=0,a,c="",u="";function f(){c&&(n===0?s.push({type:0,value:c}):n===1||n===2||n===3?(s.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=a}for(;l{i(T)}:Nn}function i(u){if(Nl(u)){const f=r.get(u);f&&(r.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(i),f.alias.forEach(i))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&r.delete(u.record.name),u.children.forEach(i),u.alias.forEach(i))}}function l(){return n}function a(u){let f=0;for(;f=0&&(u.record.path!==n[f].record.path||!Hl(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!ni(u)&&r.set(u.record.name,u)}function c(u,f){let d,m={},g,b;if("name"in u&&u.name){if(d=r.get(u.name),!d)throw yn(1,{location:u});b=d.record.name,m=me(ti(f.params,d.keys.filter(T=>!T.optional).concat(d.parent?d.parent.keys.filter(T=>T.optional):[]).map(T=>T.name)),u.params&&ti(u.params,d.keys.map(T=>T.name))),g=d.stringify(m)}else if(u.path!=null)g=u.path,d=n.find(T=>T.re.test(g)),d&&(m=d.parse(g),b=d.record.name);else{if(d=f.name?r.get(f.name):n.find(T=>T.re.test(f.path)),!d)throw yn(1,{location:u,currentLocation:f});b=d.record.name,m=me({},f.params,u.params),g=d.stringify(m)}const E=[];let A=d;for(;A;)E.unshift(A.record),A=A.parent;return{name:b,path:g,params:m,matched:E,meta:Uf(E)}}return e.forEach(u=>s(u)),{addRoute:s,resolve:c,removeRoute:i,getRoutes:l,getRecordMatcher:o}}function ti(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function zf(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:jf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function jf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function ni(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Uf(e){return e.reduce((t,n)=>me(t,n.meta),{})}function ri(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Hl(e,t){return t.children.some(n=>n===e||Hl(e,n))}function Wf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&Lo(s)):[r&&Lo(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function Vf(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=lt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Kf=Symbol(""),si=Symbol(""),Kr=Symbol(""),Jo=Symbol(""),Ao=Symbol("");function xn(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function It(e,t,n,r,o,s=i=>i()){const i=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((l,a)=>{const c=d=>{d===!1?a(yn(4,{from:n,to:t})):d instanceof Error?a(d):Pf(d)?a(yn(2,{from:t,to:d})):(i&&r.enterCallbacks[o]===i&&typeof d=="function"&&i.push(d),l())},u=s(()=>e.call(r&&r.instances[o],t,n,c));let f=Promise.resolve(u);e.length<3&&(f=f.then(c)),f.catch(d=>a(d))})}function co(e,t,n,r,o=s=>s()){const s=[];for(const i of e)for(const l in i.components){let a=i.components[l];if(!(t!=="beforeRouteEnter"&&!i.instances[l]))if(qf(a)){const u=(a.__vccOpts||a)[t];u&&s.push(It(u,n,r,i,l,o))}else{let c=a();s.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${l}" at "${i.path}"`));const f=Qu(u)?u.default:u;i.components[l]=f;const m=(f.__vccOpts||f)[t];return m&&It(m,n,r,i,l,o)()}))}}return s}function qf(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function ii(e){const t=ze(Kr),n=ze(Jo),r=I(()=>t.resolve(X(e.to))),o=I(()=>{const{matched:a}=r.value,{length:c}=a,u=a[c-1],f=n.matched;if(!u||!f.length)return-1;const d=f.findIndex(vn.bind(null,u));if(d>-1)return d;const m=li(a[c-2]);return c>1&&li(u)===m&&f[f.length-1].path!==m?f.findIndex(vn.bind(null,a[c-2])):d}),s=I(()=>o.value>-1&&Xf(n.params,r.value.params)),i=I(()=>o.value>-1&&o.value===n.matched.length-1&&Il(n.params,r.value.params));function l(a={}){return Jf(a)?t[X(e.replace)?"replace":"push"](X(e.to)).catch(Nn):Promise.resolve()}return{route:r,href:I(()=>r.value.href),isActive:s,isExactActive:i,navigate:l}}const Gf=pe({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:ii,setup(e,{slots:t}){const n=Xn(ii(e)),{options:r}=ze(Kr),o=I(()=>({[ai(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[ai(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:oe("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),Yf=Gf;function Jf(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Xf(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!lt(o)||o.length!==r.length||r.some((s,i)=>s!==o[i]))return!1}return!0}function li(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const ai=(e,t,n)=>e??t??n,Qf=pe({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=ze(Ao),o=I(()=>e.route||r.value),s=ze(si,0),i=I(()=>{let c=X(s);const{matched:u}=o.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),l=I(()=>o.value.matched[i.value]);Mt(si,I(()=>i.value+1)),Mt(Kf,l),Mt(Ao,o);const a=ce();return Fe(()=>[a.value,l.value,e.name],([c,u,f],[d,m,g])=>{u&&(u.instances[f]=c,m&&m!==u&&c&&c===d&&(u.leaveGuards.size||(u.leaveGuards=m.leaveGuards),u.updateGuards.size||(u.updateGuards=m.updateGuards))),c&&u&&(!m||!vn(u,m)||!d)&&(u.enterCallbacks[f]||[]).forEach(b=>b(c))},{flush:"post"}),()=>{const c=o.value,u=e.name,f=l.value,d=f&&f.components[u];if(!d)return ci(n.default,{Component:d,route:c});const m=f.props[u],g=m?m===!0?c.params:typeof m=="function"?m(c):m:null,E=oe(d,me({},g,t,{onVnodeUnmounted:A=>{A.component.isUnmounted&&(f.instances[u]=null)},ref:a}));return ci(n.default,{Component:E,route:c})||E}}});function ci(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Zf=Qf;function ed(e){const t=Df(e.routes,e),n=e.parseQuery||Wf,r=e.stringifyQuery||oi,o=e.history,s=xn(),i=xn(),l=xn(),a=on(_t);let c=_t;cn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=lo.bind(null,x=>""+x),f=lo.bind(null,df),d=lo.bind(null,Un);function m(x,U){let D,q;return Nl(x)?(D=t.getRecordMatcher(x),q=U):q=x,t.addRoute(q,D)}function g(x){const U=t.getRecordMatcher(x);U&&t.removeRoute(U)}function b(){return t.getRoutes().map(x=>x.record)}function E(x){return!!t.getRecordMatcher(x)}function A(x,U){if(U=me({},U||a.value),typeof x=="string"){const p=ao(n,x,U.path),_=t.resolve({path:p.path},U),k=o.createHref(p.fullPath);return me(p,_,{params:d(_.params),hash:Un(p.hash),redirectedFrom:void 0,href:k})}let D;if(x.path!=null)D=me({},x,{path:ao(n,x.path,U.path).path});else{const p=me({},x.params);for(const _ in p)p[_]==null&&delete p[_];D=me({},x,{params:f(p)}),U.params=f(U.params)}const q=t.resolve(D,U),ue=x.hash||"";q.params=u(d(q.params));const ge=mf(r,me({},x,{hash:cf(ue),path:q.path})),h=o.createHref(ge);return me({fullPath:ge,hash:ue,query:r===oi?Vf(x.query):x.query||{}},q,{redirectedFrom:void 0,href:h})}function T(x){return typeof x=="string"?ao(n,x,a.value.path):me({},x)}function v(x,U){if(c!==x)return yn(8,{from:U,to:x})}function C(x){return H(x)}function B(x){return C(me(T(x),{replace:!0}))}function O(x){const U=x.matched[x.matched.length-1];if(U&&U.redirect){const{redirect:D}=U;let q=typeof D=="function"?D(x):D;return typeof q=="string"&&(q=q.includes("?")||q.includes("#")?q=T(q):{path:q},q.params={}),me({query:x.query,hash:x.hash,params:q.path!=null?{}:x.params},q)}}function H(x,U){const D=c=A(x),q=a.value,ue=x.state,ge=x.force,h=x.replace===!0,p=O(D);if(p)return H(me(T(p),{state:typeof p=="object"?me({},ue,p.state):ue,force:ge,replace:h}),U||D);const _=D;_.redirectedFrom=U;let k;return!ge&&gf(r,q,D)&&(k=yn(16,{to:_,from:q}),We(q,q,!0,!1)),(k?Promise.resolve(k):L(_,q)).catch(S=>yt(S)?yt(S,2)?S:He(S):V(S,_,q)).then(S=>{if(S){if(yt(S,2))return H(me({replace:h},T(S.to),{state:typeof S.to=="object"?me({},ue,S.to.state):ue,force:ge}),U||_)}else S=w(_,q,!0,h,ue);return W(_,q,S),S})}function y(x,U){const D=v(x,U);return D?Promise.reject(D):Promise.resolve()}function G(x){const U=St.values().next().value;return U&&typeof U.runWithContext=="function"?U.runWithContext(x):x()}function L(x,U){let D;const[q,ue,ge]=td(x,U);D=co(q.reverse(),"beforeRouteLeave",x,U);for(const p of q)p.leaveGuards.forEach(_=>{D.push(It(_,x,U))});const h=y.bind(null,x,U);return D.push(h),Oe(D).then(()=>{D=[];for(const p of s.list())D.push(It(p,x,U));return D.push(h),Oe(D)}).then(()=>{D=co(ue,"beforeRouteUpdate",x,U);for(const p of ue)p.updateGuards.forEach(_=>{D.push(It(_,x,U))});return D.push(h),Oe(D)}).then(()=>{D=[];for(const p of ge)if(p.beforeEnter)if(lt(p.beforeEnter))for(const _ of p.beforeEnter)D.push(It(_,x,U));else D.push(It(p.beforeEnter,x,U));return D.push(h),Oe(D)}).then(()=>(x.matched.forEach(p=>p.enterCallbacks={}),D=co(ge,"beforeRouteEnter",x,U,G),D.push(h),Oe(D))).then(()=>{D=[];for(const p of i.list())D.push(It(p,x,U));return D.push(h),Oe(D)}).catch(p=>yt(p,8)?p:Promise.reject(p))}function W(x,U,D){l.list().forEach(q=>G(()=>q(x,U,D)))}function w(x,U,D,q,ue){const ge=v(x,U);if(ge)return ge;const h=U===_t,p=cn?history.state:{};D&&(q||h?o.replace(x.fullPath,me({scroll:h&&p&&p.scroll},ue)):o.push(x.fullPath,ue)),a.value=x,We(x,U,D,h),He()}let M;function te(){M||(M=o.listen((x,U,D)=>{if(!ut.listening)return;const q=A(x),ue=O(q);if(ue){H(me(ue,{replace:!0}),q).catch(Nn);return}c=q;const ge=a.value;cn&&Sf(Js(ge.fullPath,D.delta),Vr()),L(q,ge).catch(h=>yt(h,12)?h:yt(h,2)?(H(h.to,q).then(p=>{yt(p,20)&&!D.delta&&D.type===Wn.pop&&o.go(-1,!1)}).catch(Nn),Promise.reject()):(D.delta&&o.go(-D.delta,!1),V(h,q,ge))).then(h=>{h=h||w(q,ge,!1),h&&(D.delta&&!yt(h,8)?o.go(-D.delta,!1):D.type===Wn.pop&&yt(h,20)&&o.go(-1,!1)),W(q,ge,h)}).catch(Nn)}))}let ie=xn(),R=xn(),Y;function V(x,U,D){He(x);const q=R.list();return q.length?q.forEach(ue=>ue(x,U,D)):console.error(x),Promise.reject(x)}function Re(){return Y&&a.value!==_t?Promise.resolve():new Promise((x,U)=>{ie.add([x,U])})}function He(x){return Y||(Y=!x,te(),ie.list().forEach(([U,D])=>x?D(x):U()),ie.reset()),x}function We(x,U,D,q){const{scrollBehavior:ue}=e;if(!cn||!ue)return Promise.resolve();const ge=!D&&xf(Js(x.fullPath,0))||(q||!D)&&history.state&&history.state.scroll||null;return _n().then(()=>ue(x,U,ge)).then(h=>h&&Cf(h)).catch(h=>V(h,x,U))}const Be=x=>o.go(x);let Ct;const St=new Set,ut={currentRoute:a,listening:!0,addRoute:m,removeRoute:g,hasRoute:E,getRoutes:b,resolve:A,options:e,push:C,replace:B,go:Be,back:()=>Be(-1),forward:()=>Be(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:R.add,isReady:Re,install(x){const U=this;x.component("RouterLink",Yf),x.component("RouterView",Zf),x.config.globalProperties.$router=U,Object.defineProperty(x.config.globalProperties,"$route",{enumerable:!0,get:()=>X(a)}),cn&&!Ct&&a.value===_t&&(Ct=!0,C(o.location).catch(ue=>{}));const D={};for(const ue in _t)Object.defineProperty(D,ue,{get:()=>a.value[ue],enumerable:!0});x.provide(Kr,U),x.provide(Jo,Ui(D)),x.provide(Ao,a);const q=x.unmount;St.add(x),x.unmount=function(){St.delete(x),St.size<1&&(c=_t,M&&M(),M=null,a.value=_t,Ct=!1,Y=!1),q()}}};function Oe(x){return x.reduce((U,D)=>U.then(()=>G(D)),Promise.resolve())}return ut}function td(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let i=0;ivn(c,l))?r.push(l):n.push(l));const a=e.matched[i];a&&(t.matched.find(c=>vn(c,a))||o.push(a))}return[n,r,o]}function sn(){return ze(Kr)}function Ut(){return ze(Jo)}var Xo=Symbol(""),vt=()=>{const e=ze(Xo);if(!e)throw new Error("useClientData() is called without provider.");return e},nd=()=>vt().pageComponent,mn=()=>vt().pageData,pt=()=>vt().pageFrontmatter,rd=()=>vt().pageHead,od=()=>vt().pageLang,sd=()=>vt().pageLayout,wn=()=>vt().routeLocale,id=()=>vt().routes,Fl=()=>vt().siteData,Qo=()=>vt().siteLocaleData,ld=Symbol(""),To=on(Ju),Vn=on(Xu),Bl=e=>{const t=qu(e);if(Vn.value[t])return t;const n=encodeURI(t);return Vn.value[n]?n:To.value[t]||To.value[n]||t},Kn=e=>{const t=Bl(e),n=Vn.value[t]??{...Vn.value["/404.html"],notFound:!0};return{path:t,notFound:!1,...n}},Zo=pe({name:"ClientOnly",setup(e,t){const n=ce(!1);return Me(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),ad=pe({name:"Content",props:{path:{type:String,required:!1,default:""}},setup(e){const t=nd(),n=I(()=>{if(!e.path)return t.value;const r=Kn(e.path);return bc(()=>r.loader().then(({comp:o})=>o))});return()=>oe(n.value)}}),ct=(e={})=>e,rr=e=>nr(e)?e:`/ios-training/${Ll(e)}`,cd=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},or=({active:e=!1,activeClass:t="route-link-active",to:n,...r},{slots:o})=>{var a;const s=sn(),i=Bl(n),l=i.startsWith("#")||i.startsWith("?")?i:rr(i);return oe("a",{...r,class:["route-link",{[t]:e}],href:l,onClick:(c={})=>{cd(c)?s.push(n).catch():Promise.resolve()}},(a=o.default)==null?void 0:a.call(o))};or.displayName="RouteLink";or.props={active:Boolean,activeClass:String,to:String};var ud="Layout",fd="en-US",qt=Xn({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageHead:(e,t,n)=>{const r=it(t.description)?t.description:n.description,o=[...Array.isArray(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return Ku(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||fd,resolvePageLayout:(e,t)=>{const n=it(e.frontmatter.layout)?e.frontmatter.layout:ud;if(!t[n])throw new Error(`[vuepress] Cannot resolve layout: ${n}`);return t[n]},resolveRouteLocale:(e,t)=>kl(e,t),resolveSiteLocaleData:(e,t)=>{var n;return{...e,...e.locales[t],head:[...((n=e.locales[t])==null?void 0:n.head)??[],...e.head??[]]}}});function qr(e){return Ri()?(xa(e),!0):!1}function gt(e){return typeof e=="function"?e():X(e)}const es=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const dd=Object.prototype.toString,hd=e=>dd.call(e)==="[object Object]",Po=()=>{};function Dl(e,t){function n(...r){return new Promise((o,s)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(s)})}return n}const zl=e=>e();function pd(e,t={}){let n,r,o=Po;const s=l=>{clearTimeout(l),o(),o=Po};return l=>{const a=gt(e),c=gt(t.maxWait);return n&&s(n),a<=0||c!==void 0&&c<=0?(r&&(s(r),r=null),Promise.resolve(l())):new Promise((u,f)=>{o=t.rejectOnCancel?f:u,c&&!r&&(r=setTimeout(()=>{n&&s(n),r=null,u(l())},c)),n=setTimeout(()=>{r&&s(r),r=null,u(l())},a)})}}function md(e=zl){const t=ce(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...s)=>{t.value&&e(...s)};return{isActive:Mr(t),pause:n,resume:r,eventFilter:o}}function gd(e){let t;function n(){return t||(t=e()),t}return n.reset=async()=>{const r=t;t=void 0,r&&await r},n}function vd(e){return e||Ur()}function yd(e,t=200,n={}){return Dl(pd(t,n),e)}function _d(e,t,n={}){const{eventFilter:r=zl,...o}=n;return Fe(e,Dl(r,t),o)}function bd(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:s,pause:i,resume:l,isActive:a}=md(r);return{stop:_d(e,t,{...o,eventFilter:s}),pause:i,resume:l,isActive:a}}function ts(e,t=!0,n){vd()?Me(e,n):t?e():_n(e)}function wd(e,t,n={}){const{immediate:r=!0}=n,o=ce(!1);let s=null;function i(){s&&(clearTimeout(s),s=null)}function l(){o.value=!1,i()}function a(...c){i(),o.value=!0,s=setTimeout(()=>{o.value=!1,s=null,e(...c)},gt(t))}return r&&(o.value=!0,es&&a()),qr(l),{isPending:Mr(o),start:a,stop:l}}function Ed(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=je(e),s=ce(e);function i(l){if(arguments.length)return s.value=l,s.value;{const a=gt(n);return s.value=s.value===a?gt(r):a,s.value}}return o?i:[s,i]}function Qt(e){var t;const n=gt(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Dt=es?window:void 0,jl=es?window.navigator:void 0;function at(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=Dt):[t,n,r,o]=e,!t)return Po;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const s=[],i=()=>{s.forEach(u=>u()),s.length=0},l=(u,f,d,m)=>(u.addEventListener(f,d,m),()=>u.removeEventListener(f,d,m)),a=Fe(()=>[Qt(t),gt(o)],([u,f])=>{if(i(),!u)return;const d=hd(f)?{...f}:f;s.push(...n.flatMap(m=>r.map(g=>l(u,m,g,d))))},{immediate:!0,flush:"post"}),c=()=>{a(),i()};return qr(c),c}function Cd(){const e=ce(!1),t=Ur();return t&&Me(()=>{e.value=!0},t),e}function Gr(e){const t=Cd();return I(()=>(t.value,!!e()))}function Ul(e,t={}){const{window:n=Dt}=t,r=Gr(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const s=ce(!1),i=c=>{s.value=c.matches},l=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},a=mc(()=>{r.value&&(l(),o=n.matchMedia(gt(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),s.value=o.matches)});return qr(()=>{a(),l(),o=void 0}),s}function ui(e,t={}){const{controls:n=!1,navigator:r=jl}=t,o=Gr(()=>r&&"permissions"in r);let s;const i=typeof e=="string"?{name:e}:e,l=ce(),a=()=>{s&&(l.value=s.state)},c=gd(async()=>{if(o.value){if(!s)try{s=await r.permissions.query(i),at(s,"change",a),a()}catch{l.value="prompt"}return s}});return c(),n?{state:l,isSupported:o,query:c}:l}function Sd(e={}){const{navigator:t=jl,read:n=!1,source:r,copiedDuring:o=1500,legacy:s=!1}=e,i=Gr(()=>t&&"clipboard"in t),l=ui("clipboard-read"),a=ui("clipboard-write"),c=I(()=>i.value||s),u=ce(""),f=ce(!1),d=wd(()=>f.value=!1,o);function m(){i.value&&A(l.value)?t.clipboard.readText().then(T=>{u.value=T}):u.value=E()}c.value&&n&&at(["copy","cut"],m);async function g(T=gt(r)){c.value&&T!=null&&(i.value&&A(a.value)?await t.clipboard.writeText(T):b(T),u.value=T,f.value=!0,d.start())}function b(T){const v=document.createElement("textarea");v.value=T??"",v.style.position="absolute",v.style.opacity="0",document.body.appendChild(v),v.select(),document.execCommand("copy"),v.remove()}function E(){var T,v,C;return(C=(v=(T=document==null?void 0:document.getSelection)==null?void 0:T.call(document))==null?void 0:v.toString())!=null?C:""}function A(T){return T==="granted"||T==="prompt"}return{isSupported:c,text:u,copied:f,copy:g}}const pr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},mr="__vueuse_ssr_handlers__",xd=Ld();function Ld(){return mr in pr||(pr[mr]=pr[mr]||{}),pr[mr]}function kd(e,t){return xd[e]||t}function Ad(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const Td={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},fi="vueuse-storage";function Wl(e,t,n,r={}){var o;const{flush:s="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:f=Dt,eventFilter:d,onError:m=L=>{console.error(L)},initOnMounted:g}=r,b=(u?on:ce)(typeof t=="function"?t():t);if(!n)try{n=kd("getDefaultStorage",()=>{var L;return(L=Dt)==null?void 0:L.localStorage})()}catch(L){m(L)}if(!n)return b;const E=gt(t),A=Ad(E),T=(o=r.serializer)!=null?o:Td[A],{pause:v,resume:C}=bd(b,()=>O(b.value),{flush:s,deep:i,eventFilter:d});f&&l&&ts(()=>{at(f,"storage",y),at(f,fi,G),g&&y()}),g||y();function B(L,W){f&&f.dispatchEvent(new CustomEvent(fi,{detail:{key:e,oldValue:L,newValue:W,storageArea:n}}))}function O(L){try{const W=n.getItem(e);if(L==null)B(W,null),n.removeItem(e);else{const w=T.write(L);W!==w&&(n.setItem(e,w),B(W,w))}}catch(W){m(W)}}function H(L){const W=L?L.newValue:n.getItem(e);if(W==null)return a&&E!=null&&n.setItem(e,T.write(E)),E;if(!L&&c){const w=T.read(W);return typeof c=="function"?c(w,E):A==="object"&&!Array.isArray(w)?{...E,...w}:w}else return typeof W!="string"?W:T.read(W)}function y(L){if(!(L&&L.storageArea!==n)){if(L&&L.key==null){b.value=E;return}if(!(L&&L.key!==e)){v();try{(L==null?void 0:L.newValue)!==T.write(b.value)&&(b.value=H(L))}catch(W){m(W)}finally{L?_n(C):C()}}}}function G(L){y(L.detail)}return b}function Pd(e){return Ul("(prefers-color-scheme: dark)",e)}function Rd(e,t,n={}){const{window:r=Dt,...o}=n;let s;const i=Gr(()=>r&&"ResizeObserver"in r),l=()=>{s&&(s.disconnect(),s=void 0)},a=I(()=>Array.isArray(e)?e.map(f=>Qt(f)):[Qt(e)]),c=Fe(a,f=>{if(l(),i.value&&r){s=new ResizeObserver(t);for(const d of f)d&&s.observe(d,o)}},{immediate:!0,flush:"post"}),u=()=>{l(),c()};return qr(u),{isSupported:i,stop:u}}function Od(e,t={width:0,height:0},n={}){const{window:r=Dt,box:o="content-box"}=n,s=I(()=>{var f,d;return(d=(f=Qt(e))==null?void 0:f.namespaceURI)==null?void 0:d.includes("svg")}),i=ce(t.width),l=ce(t.height),{stop:a}=Rd(e,([f])=>{const d=o==="border-box"?f.borderBoxSize:o==="content-box"?f.contentBoxSize:f.devicePixelContentBoxSize;if(r&&s.value){const m=Qt(e);if(m){const g=r.getComputedStyle(m);i.value=Number.parseFloat(g.width),l.value=Number.parseFloat(g.height)}}else if(d){const m=Array.isArray(d)?d:[d];i.value=m.reduce((g,{inlineSize:b})=>g+b,0),l.value=m.reduce((g,{blockSize:b})=>g+b,0)}else i.value=f.contentRect.width,l.value=f.contentRect.height},n);ts(()=>{const f=Qt(e);f&&(i.value="offsetWidth"in f?f.offsetWidth:t.width,l.value="offsetHeight"in f?f.offsetHeight:t.height)});const c=Fe(()=>Qt(e),f=>{i.value=f?t.width:0,l.value=f?t.height:0});function u(){a(),c()}return{width:i,height:l,stop:u}}function Id(e={}){const{window:t=Dt,behavior:n="auto"}=e;if(!t)return{x:ce(0),y:ce(0)};const r=ce(t.scrollX),o=ce(t.scrollY),s=I({get(){return r.value},set(l){scrollTo({left:l,behavior:n})}}),i=I({get(){return o.value},set(l){scrollTo({top:l,behavior:n})}});return at(t,"scroll",()=>{r.value=t.scrollX,o.value=t.scrollY},{capture:!1,passive:!0}),{x:s,y:i}}function $d(e={}){const{window:t=Dt,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:s=!0}=e,i=ce(n),l=ce(r),a=()=>{t&&(s?(i.value=t.innerWidth,l.value=t.innerHeight):(i.value=t.document.documentElement.clientWidth,l.value=t.document.documentElement.clientHeight))};if(a(),ts(a),at("resize",a,{passive:!0}),o){const c=Ul("(orientation: portrait)");Fe(c,()=>a())}return{width:i,height:l}}const di=async(e,t)=>{const{path:n,query:r}=e.currentRoute.value,{scrollBehavior:o}=e.options;e.options.scrollBehavior=void 0,await e.replace({path:n,query:r,hash:t}),e.options.scrollBehavior=o},Nd=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=sn();at("scroll",yd(()=>{var g,b;const i=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(i-0)f.some(A=>A.hash===E.hash));for(let E=0;E=(((g=A.parentElement)==null?void 0:g.offsetTop)??0)-r,C=!T||i<(((b=T.parentElement)==null?void 0:b.offsetTop)??0)-r;if(!(v&&C))continue;const O=decodeURIComponent(o.currentRoute.value.hash),H=decodeURIComponent(A.hash);if(O===H)return;if(u){for(let y=E+1;y{const t=wn();return I(()=>e[t.value]??{})},zd=()=>{const e=id();return I(()=>Object.keys(e.value))},uo=(e,t)=>{var r;const n=(r=(t==null?void 0:t._instance)||Ur())==null?void 0:r.appContext.components;return n?e in n||tt(e)in n||Yn(tt(e))in n:!1},jd=(e,t)=>it(e)&&e.startsWith(t),Vl=e=>jd(e,"/"),Ud="http://.",hi=(e,t)=>{if(Vl(e)||typeof t!="string")return Kn(e);const n=t.slice(0,t.lastIndexOf("/"));return Kn(new URL(`${n}/${encodeURI(e)}`,Ud).pathname)},Kl=e=>new Promise(t=>setTimeout(t,e));var Wd={"/":{backToTop:"Back to top"}};const Vd=pe({name:"BackToTop",setup(){const e=pt(),t=ns(Wd),n=on(),{height:r}=Od(n),{height:o}=$d(),{y:s}=Id(),i=I(()=>e.value.backToTop!==!1&&s.value>100),l=I(()=>s.value/(r.value-o.value)*100);return Me(()=>{n.value=document.body}),()=>oe(bn,{name:"back-to-top"},()=>i.value?oe("button",{type:"button",class:"vp-back-to-top-button","aria-label":t.value.backToTop,onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[oe("span",{class:"vp-scroll-progress",role:"progressbar","aria-labelledby":"loadinglabel","aria-valuenow":l.value},oe("svg",oe("circle",{cx:"26",cy:"26",r:"24",fill:"none",stroke:"currentColor","stroke-width":"4","stroke-dasharray":`${Math.PI*l.value*.48} ${Math.PI*(100-l.value)*.48}`}))),oe("div",{class:"back-to-top-icon"})]):null)}}),Kd=ct({rootComponents:[Vd]}),qd=/\b(?:Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini)/i,Gd=()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator&&qd.test(navigator.userAgent),Yd=({delay:e=500,duration:t=2e3,locales:n,selector:r,showInMobile:o})=>{const{copy:s,copied:i}=Sd({legacy:!0,copiedDuring:t}),l=ns(n),a=mn(),c=d=>{if(!d.hasAttribute("copy-code-registered")){const m=document.createElement("button");m.type="button",m.classList.add("vp-copy-code-button"),m.innerHTML='
',m.setAttribute("aria-label",l.value.copy),m.setAttribute("data-copied",l.value.copied),d.parentElement&&d.parentElement.insertBefore(m,d),d.setAttribute("copy-code-registered","")}},u=()=>{_n().then(()=>Kl(e)).then(()=>{r.forEach(d=>{document.querySelectorAll(d).forEach(c)})})},f=(d,m,g)=>{let{innerText:b=""}=m;/language-(shellscript|shell|bash|sh|zsh)/.test(d.classList.toString())&&(b=b.replace(/^ *(\$|>) /gm,"")),s(b).then(()=>{g.classList.add("copied"),Fe(i,()=>{g.classList.remove("copied"),g.blur()},{once:!0})})};Me(()=>{const d=!Gd()||o;d&&u(),at("click",m=>{const g=m.target;if(g.matches('div[class*="language-"] > button.copy')){const b=g.parentElement,E=g.nextElementSibling;E&&f(b,E,g)}else if(g.matches('div[class*="language-"] div.vp-copy-icon')){const b=g.parentElement,E=b.parentElement,A=b.nextElementSibling;A&&f(E,A,b)}}),Fe(()=>a.value.path,()=>{d&&u()})})};var Jd={"/":{copy:"Copy code",copied:"Copied"}},Xd=['.theme-default-content div[class*="language-"] pre'];const Qd=500,Zd=2e3,eh=Jd,th=Xd,nh=!1,rh=ct({setup:()=>{Yd({selector:th,locales:eh,duration:Zd,delay:Qd,showInMobile:nh})}}),oh=oe("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[oe("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),oe("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),sh=pe({name:"ExternalLinkIcon",props:{locales:{type:Object,default:()=>({})}},setup(e){const t=wn(),n=I(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>oe("span",[oh,oe("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}});var ih={"/":{openInNewWindow:"open in new window"},"/fr/":{openInNewWindow:"open in new window"}};const lh=ih,ah=ct({enhance({app:e}){e.component("ExternalLinkIcon",oe(sh,{locales:lh}))}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const fe={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=fe.isStarted();e=fo(e,fe.settings.minimum,1),fe.status=e===1?null:e;const n=fe.render(!t),r=n.querySelector(fe.settings.barSelector),o=fe.settings.speed,s=fe.settings.easing;return n.offsetWidth,ch(i=>{gr(r,{transform:"translate3d("+pi(e)+"%,0,0)",transition:"all "+o+"ms "+s}),e===1?(gr(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){gr(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){fe.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),fe},isStarted:()=>typeof fe.status=="number",start:()=>{fe.status||fe.set(0);const e=()=>{setTimeout(()=>{fe.status&&(fe.trickle(),e())},fe.settings.trickleSpeed)};return fe.settings.trickle&&e(),fe},done:e=>!e&&!fe.status?fe:fe.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=fe.status;return t?(typeof e!="number"&&(e=(1-t)*fo(Math.random()*t,.1,.95)),t=fo(t+e,0,.994),fe.set(t)):fe.start()},trickle:()=>fe.inc(Math.random()*fe.settings.trickleRate),render:e=>{if(fe.isRendered())return document.getElementById("nprogress");mi(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=fe.settings.template;const n=t.querySelector(fe.settings.barSelector),r=e?"-100":pi(fe.status||0),o=document.querySelector(fe.settings.parent);return gr(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&mi(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{gi(document.documentElement,"nprogress-busy"),gi(document.querySelector(fe.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&uh(e)},isRendered:()=>!!document.getElementById("nprogress")},fo=(e,t,n)=>en?n:e,pi=e=>(-1+e)*100,ch=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),gr=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(l,a){return a.toUpperCase()})}function r(i){const l=document.body.style;if(i in l)return i;let a=e.length;const c=i.charAt(0).toUpperCase()+i.slice(1);let u;for(;a--;)if(u=e[a]+c,u in l)return u;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function s(i,l,a){l=o(l),i.style[l]=a}return function(i,l){for(const a in l){const c=l[a];c!==void 0&&Object.prototype.hasOwnProperty.call(l,a)&&s(i,a,c)}}}(),ql=(e,t)=>(typeof e=="string"?e:rs(e)).indexOf(" "+t+" ")>=0,mi=(e,t)=>{const n=rs(e),r=n+t;ql(n,t)||(e.className=r.substring(1))},gi=(e,t)=>{const n=rs(e);if(!ql(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},rs=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),uh=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)},fh=()=>{Me(()=>{const e=sn(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||fe.start()}),e.afterEach(n=>{t.add(n.path),fe.done()})})},dh=ct({setup(){fh()}}),hh=JSON.parse(`{"logo":"/logo.png","repo":"worldline/ios-training","locales":{"/":{"selectLanguageName":"English","sidebar":["/","/presentation/","/swift-part1/","/swift-part2/","/ui-development/","/api-communication/","/persist-data/","/mini-project/","/to-go-further/"]},"/fr/":{"selectLanguageName":"Français","sidebar":["/fr/","/fr/presentation/","/fr/swift-part1/","/fr/swift-part2/","/fr/ui-development/","/fr/api-communication/","/fr/persist-data/","/fr/mini-project/","/fr/to-go-further/"]}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"auto","sidebarDepth":2,"editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),ph=ce(hh),Gl=()=>ph,Yl=Symbol(""),mh=()=>{const e=ze(Yl);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},gh=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},vh=ct({enhance({app:e}){const t=Gl(),n=e._context.provides[Xo],r=I(()=>gh(t.value,n.routeLocale.value));e.provide(Yl,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}}),yh=pe({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(j(),Z("span",{class:Ke(["badge",e.type]),style:Jn({verticalAlign:e.vertical})},[ye(t.$slots,"default",{},()=>[mt(Ce(e.text),1)])],6))}}),Se=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},_h=Se(yh,[["__file","Badge.vue"]]),bh=pe({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const n=ce([]),r=ce(-1),o=Wl("vuepress-code-group",{}),s=I(()=>n.value.map(c=>c.innerText).join(","));Me(()=>{Fe(()=>o.value[s.value],(c=-1)=>{r.value!==c&&(r.value=c)},{immediate:!0}),Fe(r,c=>{o.value[s.value]!==c&&(o.value[s.value]=c)})});const i=(c=r.value)=>{c{c>0?r.value=c-1:r.value=n.value.length-1,n.value[r.value].focus()},a=(c,u)=>{c.key===" "||c.key==="Enter"?(c.preventDefault(),r.value=u):c.key==="ArrowRight"?(c.preventDefault(),i(u)):c.key==="ArrowLeft"&&(c.preventDefault(),l(u))};return()=>{var u;const c=(((u=t.default)==null?void 0:u.call(t))||[]).filter(f=>f.type.name==="CodeGroupItem").map(f=>(f.props===null&&(f.props={}),f));return c.length===0?null:(r.value<0||r.value>c.length-1?(r.value=c.findIndex(f=>f.props.active===""||f.props.active===!0),r.value===-1&&(r.value=0)):c.forEach((f,d)=>{f.props.active=d===r.value}),oe("div",{class:"code-group"},[oe("div",{class:"code-group__nav",role:"tablist"},c.map((f,d)=>{const m=d===r.value;return oe("button",{ref:g=>{g&&(n.value[d]=g)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":m},role:"tab",ariaSelected:m,onClick:()=>r.value=d,onKeydown:g=>a(g,d)},f.props.title)})),c]))}}}),wh=pe({name:"CodeGroupItem",__name:"CodeGroupItem",props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(j(),Z("div",{class:Ke(["code-group-item",{"code-group-item__active":e.active}]),role:"tabpanel"},[ye(t.$slots,"default")],2))}}),Eh=Se(wh,[["__file","CodeGroupItem.vue"]]),Ch=()=>Gl(),Ne=()=>mh(),Jl=Symbol(""),os=()=>{const e=ze(Jl);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Sh=()=>{const e=Ne(),t=Pd(),n=Wl("vuepress-color-scheme",e.value.colorMode),r=I({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(o){o===t.value?n.value="auto":n.value=o?"dark":"light"}});Mt(Jl,r),xh(r)},xh=e=>{const t=(n=e.value)=>{const r=window==null?void 0:window.document.querySelector("html");r==null||r.classList.toggle("dark",n)};Me(()=>{Fe(e,t,{immediate:!0})}),jr(()=>t())},Lh="http://.",kh=()=>{const e=sn(),t=Ut();return n=>{if(n)if(Vl(n))t.path!==n&&e.push(n);else if(Sl(n))window&&window.open(n);else{const r=t.path.slice(0,t.path.lastIndexOf("/"));e.push(new URL(`${r}/${encodeURI(n)}`,Lh).pathname)}}};let ho=null,Ln=null;const Ah={wait:()=>ho,pending:()=>{ho=new Promise(e=>Ln=e)},resolve:()=>{Ln==null||Ln(),ho=null,Ln=null}},Xl=()=>Ah,Ql=e=>{const{notFound:t,meta:n,path:r}=Kn(e);return t?{text:r,link:r}:{text:n.title||r,link:r}},vi=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Th=(e,t)=>{if(t.hash===e)return!0;const n=vi(t.path),r=vi(e);return n===r},Zl=(e,t)=>e.link&&Th(e.link,t)?!0:e.children?e.children.some(n=>Zl(n,t)):!1,ea=e=>!nr(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,Ph={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Rh=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=ea(e);return n!==null?Ph[n]:null},Oh=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const s=Rh({docsRepo:e,editLinkPattern:o});return s?s.replace(/:repo/,nr(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Ll(`${xl(n)}/${r}`)):null},ta=Symbol("sidebarItems"),ss=()=>{const e=ze(ta);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},Ih=()=>{const e=Ne(),t=pt(),n=mn(),r=Ut(),o=I(()=>$h(t.value,e.value,n.value,r.path));Mt(ta,o)},$h=(e,t,n,r)=>{const o=e.sidebar??t.sidebar??"auto",s=e.sidebarDepth??t.sidebarDepth??2;return e.home||o===!1?[]:o==="auto"?na(n,s):Array.isArray(o)?ra(n,r,o,s):Go(o)?Mh(n,r,o,s):[]},Nh=(e,t)=>({text:e.title,link:e.link,children:is(e.children,t)}),is=(e,t)=>t>0?e.map(n=>Nh(n,t-1)):[],na=(e,t)=>[{text:e.title,children:is(e.headers,t)}],ra=(e,t,n,r)=>{const o=s=>{var l;let i;if(it(s)?i=Ql(s):i=s,i.children)return{...i,children:i.children.map(a=>o(a))};if(i.link===t){const a=((l=e.headers[0])==null?void 0:l.level)===1?e.headers[0].children:e.headers;return{...i,children:is(a,r)}}return i};return n.map(s=>o(s))},Mh=(e,t,n,r)=>{const o=kl(n,t),s=n[o]??[];return s==="heading"?na(e,r):ra(e,t,s,r)},Hh="719px",Fh={mobile:Hh};var qn;(function(e){e.MOBILE="mobile"})(qn||(qn={}));var Ei;const Bh={[qn.MOBILE]:Number.parseInt((Ei=Fh.mobile)==null?void 0:Ei.replace("px",""),10)},oa=(e,t)=>{const n=Bh[e];Number.isInteger(n)&&(at("orientationchange",()=>t(n),!1),at("resize",()=>t(n),!1),Me(()=>{t(n)}))},Dh={},zh={class:"theme-default-content"};function jh(e,t){const n=tn("Content");return j(),Z("div",zh,[se(n)])}const Uh=Se(Dh,[["render",jh],["__file","HomeContent.vue"]]),Wh={key:0,class:"features"},Vh=pe({__name:"HomeFeatures",setup(e){const t=pt(),n=I(()=>Array.isArray(t.value.features)?t.value.features:[]);return(r,o)=>n.value.length?(j(),Z("div",Wh,[(j(!0),Z(_e,null,Ft(n.value,s=>(j(),Z("div",{key:s.title,class:"feature"},[ne("h2",null,Ce(s.title),1),ne("p",null,Ce(s.details),1)]))),128))])):ke("",!0)}}),Kh=Se(Vh,[["__file","HomeFeatures.vue"]]),qh=["innerHTML"],Gh=["textContent"],Yh=pe({__name:"HomeFooter",setup(e){const t=pt(),n=I(()=>t.value.footer),r=I(()=>t.value.footerHtml);return(o,s)=>n.value?(j(),Z(_e,{key:0},[r.value?(j(),Z("div",{key:0,class:"footer",innerHTML:n.value},null,8,qh)):(j(),Z("div",{key:1,class:"footer",textContent:Ce(n.value)},null,8,Gh))],64)):ke("",!0)}}),Jh=Se(Yh,[["__file","HomeFooter.vue"]]),Xh=["href","rel","target","aria-label"],Qh=pe({inheritAttrs:!1,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,n=Ut(),r=Fl(),{item:o}=Hr(t),s=I(()=>nr(o.value.link)),i=I(()=>!s.value&&Sl(o.value.link)),l=I(()=>{if(!i.value){if(o.value.target)return o.value.target;if(s.value)return"_blank"}}),a=I(()=>l.value==="_blank"),c=I(()=>!s.value&&!i.value&&!a.value),u=I(()=>{if(!i.value){if(o.value.rel)return o.value.rel;if(a.value)return"noopener noreferrer"}}),f=I(()=>o.value.ariaLabel||o.value.text),d=I(()=>{const g=Object.keys(r.value.locales);return g.length?!g.some(b=>b===o.value.link):o.value.link!=="/"}),m=I(()=>c.value?o.value.activeMatch?new RegExp(o.value.activeMatch).test(n.path):d.value?n.path.startsWith(o.value.link):!1:!1);return(g,b)=>{const E=tn("RouteLink"),A=tn("AutoLinkExternalIcon");return c.value?(j(),xe(E,So({key:0,active:m.value,to:X(o).link,"aria-label":f.value},g.$attrs),{default:Le(()=>[ye(g.$slots,"default",{},()=>[ye(g.$slots,"before"),mt(" "+Ce(X(o).text)+" ",1),ye(g.$slots,"after")])]),_:3},16,["active","to","aria-label"])):(j(),Z("a",So({key:1,class:"external-link",href:X(o).link,rel:u.value,target:l.value,"aria-label":f.value},g.$attrs),[ye(g.$slots,"default",{},()=>[ye(g.$slots,"before"),mt(" "+Ce(X(o).text)+" ",1),a.value?(j(),xe(A,{key:0})):ke("",!0),ye(g.$slots,"after")])],16,Xh))}}}),wt=Se(Qh,[["__file","AutoLink.vue"]]),Zh={class:"hero"},ep={key:0,id:"main-title"},tp={key:1,class:"description"},np={key:2,class:"actions"},rp=pe({__name:"HomeHero",setup(e){const t=pt(),n=Qo(),r=os(),o=I(()=>r.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),s=I(()=>t.value.heroAlt||l.value||"hero"),i=I(()=>t.value.heroHeight||280),l=I(()=>t.value.heroText===null?null:t.value.heroText||n.value.title||"Hello"),a=I(()=>t.value.tagline===null?null:t.value.tagline||n.value.description||"Welcome to your VuePress site"),c=I(()=>Array.isArray(t.value.actions)?t.value.actions.map(({text:f,link:d,type:m="primary"})=>({text:f,link:d,type:m})):[]),u=()=>{if(!o.value)return null;const f=oe("img",{src:rr(o.value),alt:s.value,height:i.value});return t.value.heroImageDark===void 0?f:oe(Zo,()=>f)};return(f,d)=>(j(),Z("header",Zh,[se(u),l.value?(j(),Z("h1",ep,Ce(l.value),1)):ke("",!0),a.value?(j(),Z("p",tp,Ce(a.value),1)):ke("",!0),c.value.length?(j(),Z("p",np,[(j(!0),Z(_e,null,Ft(c.value,m=>(j(),xe(wt,{key:m.text,class:Ke(["action-button",[m.type]]),item:m},null,8,["class","item"]))),128))])):ke("",!0)]))}}),op=Se(rp,[["__file","HomeHero.vue"]]),sp={class:"home"},ip=pe({__name:"Home",setup(e){return(t,n)=>(j(),Z("main",sp,[se(op),se(Kh),se(Uh),se(Jh)]))}}),lp=Se(ip,[["__file","Home.vue"]]),ap=["aria-hidden"],cp=pe({__name:"NavbarBrand",setup(e){const t=wn(),n=Qo(),r=Ne(),o=os(),s=I(()=>r.value.home||t.value),i=I(()=>n.value.title),l=I(()=>o.value&&r.value.logoDark!==void 0?r.value.logoDark:r.value.logo),a=I(()=>r.value.logoAlt??i.value),c=I(()=>i.value.toLocaleUpperCase().trim()===a.value.toLocaleUpperCase().trim()),u=()=>{if(!l.value)return null;const f=oe("img",{class:"logo",src:rr(l.value),alt:a.value});return r.value.logoDark===void 0?f:oe(Zo,()=>f)};return(f,d)=>(j(),xe(X(or),{to:s.value},{default:Le(()=>[se(u),i.value?(j(),Z("span",{key:0,class:Ke(["site-name",{"can-hide":l.value}]),"aria-hidden":c.value},Ce(i.value),11,ap)):ke("",!0)]),_:1},8,["to"]))}}),up=Se(cp,[["__file","NavbarBrand.vue"]]),fp=pe({__name:"DropdownTransition",setup(e){const t=r=>{r.style.height=r.scrollHeight+"px"},n=r=>{r.style.height=""};return(r,o)=>(j(),xe(bn,{name:"dropdown",onEnter:t,onAfterEnter:n,onBeforeLeave:t},{default:Le(()=>[ye(r.$slots,"default")]),_:3}))}}),sa=Se(fp,[["__file","DropdownTransition.vue"]]),dp=["aria-label"],hp={class:"title"},pp=ne("span",{class:"arrow down"},null,-1),mp=["aria-label"],gp={class:"title"},vp={class:"navbar-dropdown"},yp={class:"navbar-dropdown-subtitle"},_p={key:1},bp={class:"navbar-dropdown-subitem-wrapper"},wp=pe({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:n}=Hr(t),r=I(()=>n.value.ariaLabel||n.value.text),o=ce(!1),s=Ut();Fe(()=>s.path,()=>{o.value=!1});const i=a=>{a.detail===0?o.value=!o.value:o.value=!1},l=(a,c)=>c[c.length-1]===a;return(a,c)=>(j(),Z("div",{class:Ke(["navbar-dropdown-wrapper",{open:o.value}])},[ne("button",{class:"navbar-dropdown-title",type:"button","aria-label":r.value,onClick:i},[ne("span",hp,Ce(X(n).text),1),pp],8,dp),ne("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":r.value,onClick:c[0]||(c[0]=u=>o.value=!o.value)},[ne("span",gp,Ce(X(n).text),1),ne("span",{class:Ke(["arrow",o.value?"down":"right"])},null,2)],8,mp),se(sa,null,{default:Le(()=>[Lr(ne("ul",vp,[(j(!0),Z(_e,null,Ft(X(n).children,u=>(j(),Z("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(j(),Z(_e,{key:0},[ne("h4",yp,[u.link?(j(),xe(wt,{key:0,item:u,onFocusout:f=>l(u,X(n).children)&&u.children.length===0&&(o.value=!1)},null,8,["item","onFocusout"])):(j(),Z("span",_p,Ce(u.text),1))]),ne("ul",bp,[(j(!0),Z(_e,null,Ft(u.children,f=>(j(),Z("li",{key:f.link,class:"navbar-dropdown-subitem"},[se(wt,{item:f,onFocusout:d=>l(f,u.children)&&l(u,X(n).children)&&(o.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(j(),xe(wt,{key:1,item:u,onFocusout:f=>l(u,X(n).children)&&(o.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[Or,o.value]])]),_:1})],2))}}),Ep=Se(wp,[["__file","NavbarDropdown.vue"]]),Cp=["aria-label"],Sp=pe({__name:"NavbarItems",setup(e){const t=()=>{const f=Ut(),d=zd(),m=wn(),g=Fl(),b=Qo(),E=Ch(),A=Ne();return I(()=>{const T=Object.keys(g.value.locales);if(T.length<2)return[];const v=f.path,C=f.fullPath;return[{text:`${A.value.selectLanguageText}`,ariaLabel:`${A.value.selectLanguageAriaLabel??A.value.selectLanguageText}`,children:T.map(O=>{var w,M;const H=((w=g.value.locales)==null?void 0:w[O])??{},y=((M=E.value.locales)==null?void 0:M[O])??{},G=`${H.lang}`,L=y.selectLanguageName??G;if(G===b.value.lang)return{text:L,activeMatch:/./,link:f.hash??"#"};const W=v.replace(m.value,O);return{text:L,link:d.value.some(te=>te===W)?C.replace(v,W):y.home??O}})}]})},n=()=>{const f=Ne(),d=I(()=>f.value.repo),m=I(()=>d.value?ea(d.value):null),g=I(()=>d.value&&!nr(d.value)?`https://github.com/${d.value}`:d.value),b=I(()=>g.value?f.value.repoLabel?f.value.repoLabel:m.value===null?"Source":m.value:null);return I(()=>!g.value||!b.value?[]:[{text:b.value,link:g.value}])},r=f=>it(f)?Ql(f):f.children?{...f,children:f.children.map(d=>r(d))}:f,o=()=>{const f=Ne();return I(()=>(f.value.navbar||[]).map(d=>r(d)))},s=ce(!1),i=o(),l=t(),a=n(),c=I(()=>[...i.value,...l.value,...a.value]);oa(qn.MOBILE,f=>{window.innerWidthNe().value.navbarLabel??"site navigation");return(f,d)=>c.value.length?(j(),Z("nav",{key:0,class:"navbar-items","aria-label":u.value},[(j(!0),Z(_e,null,Ft(c.value,m=>(j(),Z("div",{key:m.text,class:"navbar-item"},["children"in m?(j(),xe(Ep,{key:0,item:m,class:Ke(s.value?"mobile":"")},null,8,["item","class"])):(j(),xe(wt,{key:1,item:m},null,8,["item"]))]))),128))],8,Cp)):ke("",!0)}}),ia=Se(Sp,[["__file","NavbarItems.vue"]]),xp=["title"],Lp={class:"icon",focusable:"false",viewBox:"0 0 32 32"},kp=eu('',9),Ap=[kp],Tp={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Pp=ne("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),Rp=[Pp],Op=pe({__name:"ToggleColorModeButton",setup(e){const t=Ne(),n=os(),r=()=>{n.value=!n.value};return(o,s)=>(j(),Z("button",{class:"toggle-color-mode-button",title:X(t).toggleColorMode,onClick:r},[Lr((j(),Z("svg",Lp,Ap,512)),[[Or,!X(n)]]),Lr((j(),Z("svg",Tp,Rp,512)),[[Or,X(n)]])],8,xp))}}),Ip=Se(Op,[["__file","ToggleColorModeButton.vue"]]),$p=["title"],Np=ne("div",{class:"icon","aria-hidden":"true"},[ne("span"),ne("span"),ne("span")],-1),Mp=[Np],Hp=pe({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=Ne();return(n,r)=>(j(),Z("div",{class:"toggle-sidebar-button",title:X(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:r[0]||(r[0]=o=>n.$emit("toggle"))},Mp,8,$p))}}),Fp=Se(Hp,[["__file","ToggleSidebarButton.vue"]]),Bp=pe({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=Ne(),n=ce(null),r=ce(null),o=ce(0),s=I(()=>o.value?{maxWidth:o.value+"px"}:{}),i=(l,a)=>{var f,d,m;const c=(m=(d=(f=l==null?void 0:l.ownerDocument)==null?void 0:f.defaultView)==null?void 0:d.getComputedStyle(l,null))==null?void 0:m[a],u=Number.parseInt(c,10);return Number.isNaN(u)?0:u};return oa(qn.MOBILE,l=>{var c;const a=i(n.value,"paddingLeft")+i(n.value,"paddingRight");window.innerWidth{const c=tn("NavbarSearch");return j(),Z("header",{ref_key:"navbar",ref:n,class:"navbar"},[se(Fp,{onToggle:a[0]||(a[0]=u=>l.$emit("toggle-sidebar"))}),ne("span",{ref_key:"navbarBrand",ref:r},[se(up)],512),ne("div",{class:"navbar-items-wrapper",style:Jn(s.value)},[ye(l.$slots,"before"),se(ia,{class:"can-hide"}),ye(l.$slots,"after"),X(t).colorModeSwitch?(j(),xe(Ip,{key:0})):ke("",!0),se(c)],4)],512)}}}),Dp=Se(Bp,[["__file","Navbar.vue"]]),zp={class:"vp-page-meta"},jp={key:0,class:"vp-meta-item edit-link"},Up=ne("svg",{class:"icon",viewBox:"0 0 1024 1024"},[ne("g",{fill:"currentColor"},[ne("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),ne("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})])],-1),Wp={class:"vp-meta-item git-info"},Vp={key:0,class:"vp-meta-item last-updated"},Kp={class:"meta-item-label"},qp={class:"meta-item-info"},Gp={key:1,class:"vp-meta-item contributors"},Yp={class:"meta-item-label"},Jp={class:"meta-item-info"},Xp=["title"],Qp=pe({__name:"PageMeta",setup(e){const t=()=>{const a=Ne(),c=mn(),u=pt();return I(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:d,docsRepo:m=d,docsBranch:g="main",docsDir:b="",editLinkText:E}=a.value;if(!m)return null;const A=Oh({docsRepo:m,docsBranch:g,docsDir:b,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return A?{text:E??"Edit this page",link:A}:null})},n=()=>{const a=Ne(),c=mn(),u=pt();return I(()=>{var m,g;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((m=c.value.git)!=null&&m.updatedTime)?null:new Date((g=c.value.git)==null?void 0:g.updatedTime).toLocaleString()})},r=()=>{const a=Ne(),c=mn(),u=pt();return I(()=>{var d;return u.value.contributors??a.value.contributors??!0?((d=c.value.git)==null?void 0:d.contributors)??null:null})},o=Ne(),s=t(),i=n(),l=r();return(a,c)=>{const u=tn("ClientOnly");return j(),Z("footer",zp,[X(s)?(j(),Z("div",jp,[se(wt,{class:"label",item:X(s)},{before:Le(()=>[Up]),_:1},8,["item"])])):ke("",!0),ne("div",Wp,[X(i)?(j(),Z("div",Vp,[ne("span",Kp,Ce(X(o).lastUpdatedText)+": ",1),se(u,null,{default:Le(()=>[ne("span",qp,Ce(X(i)),1)]),_:1})])):ke("",!0),X(l)&&X(l).length?(j(),Z("div",Gp,[ne("span",Yp,Ce(X(o).contributorsText)+": ",1),ne("span",Jp,[(j(!0),Z(_e,null,Ft(X(l),(f,d)=>(j(),Z(_e,{key:d},[ne("span",{class:"contributor",title:`email: ${f.email}`},Ce(f.name),9,Xp),d!==X(l).length-1?(j(),Z(_e,{key:0},[mt(", ")],64)):ke("",!0)],64))),128))])])):ke("",!0)])])}}}),Zp=Se(Qp,[["__file","PageMeta.vue"]]),em=["aria-label"],tm={class:"hint"},nm=ne("span",{class:"arrow left"},null,-1),rm={class:"link"},om={class:"hint"},sm=ne("span",{class:"arrow right"},null,-1),im={class:"link"},lm=pe({__name:"PageNav",setup(e){const t=(f,d)=>{if(f===!1)return null;if(it(f)){const{notFound:m,meta:g,path:b}=hi(f,d);return m?{text:b,link:b}:{text:g.title||b,link:b}}return Go(f)?{...f,link:hi(f.link,d).path}:!1},n=(f,d,m)=>{const g=f.findIndex(b=>b.link===d);if(g!==-1){const b=f[g+m];return b!=null&&b.link?b:null}for(const b of f)if(b.children){const E=n(b.children,d,m);if(E)return E}return null},r=pt(),o=ss(),s=Ne(),i=Ut(),l=kh(),a=I(()=>{const f=t(r.value.prev,i.path);return f!==!1?f:n(o.value,i.path,-1)}),c=I(()=>{const f=t(r.value.next,i.path);return f!==!1?f:n(o.value,i.path,1)}),u=I(()=>Ne().value.pageNavbarLabel??"page navigation");return at("keydown",f=>{f.altKey&&(f.key==="ArrowRight"?c.value&&(l(c.value.link),f.preventDefault()):f.key==="ArrowLeft"&&a.value&&(l(a.value.link),f.preventDefault()))}),(f,d)=>a.value||c.value?(j(),Z("nav",{key:0,class:"vp-page-nav","aria-label":u.value},[a.value?(j(),xe(wt,{key:0,class:"prev",item:a.value},{default:Le(()=>[ne("div",tm,[nm,mt(" "+Ce(X(s).prev??"Prev"),1)]),ne("div",rm,[ne("span",null,Ce(a.value.text),1)])]),_:1},8,["item"])):ke("",!0),c.value?(j(),xe(wt,{key:1,class:"next",item:c.value},{default:Le(()=>[ne("div",om,[mt(Ce(X(s).next??"Next")+" ",1),sm]),ne("div",im,[ne("span",null,Ce(c.value.text),1)])]),_:1},8,["item"])):ke("",!0)],8,em)):ke("",!0)}}),am=Se(lm,[["__file","PageNav.vue"]]),cm={class:"page"},um={class:"theme-default-content"},fm=pe({__name:"Page",setup(e){return(t,n)=>{const r=tn("Content");return j(),Z("main",cm,[ye(t.$slots,"top"),ne("div",um,[ye(t.$slots,"content-top"),se(r),ye(t.$slots,"content-bottom")]),se(Zp),se(am),ye(t.$slots,"bottom")])}}}),dm=Se(fm,[["__file","Page.vue"]]),hm={class:"sidebar-item-children"},pm=pe({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:n,depth:r}=Hr(t),o=Ut(),s=sn(),i=I(()=>Zl(n.value,o)),l=I(()=>({"sidebar-item":!0,"sidebar-heading":r.value===0,active:i.value,collapsible:n.value.collapsible})),a=I(()=>n.value.collapsible?i.value:!0),[c,u]=Ed(a.value),f=m=>{n.value.collapsible&&(m.preventDefault(),u())},d=s.afterEach(m=>{_n(()=>{c.value=a.value})});return zr(()=>{d()}),(m,g)=>{var E;const b=tn("SidebarItem",!0);return j(),Z("li",null,[X(n).link?(j(),xe(wt,{key:0,class:Ke(l.value),item:X(n)},null,8,["class","item"])):(j(),Z("p",{key:1,tabindex:"0",class:Ke(l.value),onClick:f,onKeydown:Hu(f,["enter"])},[mt(Ce(X(n).text)+" ",1),X(n).collapsible?(j(),Z("span",{key:0,class:Ke(["arrow",X(c)?"down":"right"])},null,2)):ke("",!0)],34)),(E=X(n).children)!=null&&E.length?(j(),xe(sa,{key:2},{default:Le(()=>[Lr(ne("ul",hm,[(j(!0),Z(_e,null,Ft(X(n).children,A=>(j(),xe(b,{key:`${X(r)}${A.text}${A.link}`,item:A,depth:X(r)+1},null,8,["item","depth"]))),128))],512),[[Or,X(c)]])]),_:1})):ke("",!0)])}}}),mm=Se(pm,[["__file","SidebarItem.vue"]]),gm={key:0,class:"sidebar-items"},vm=pe({__name:"SidebarItems",setup(e){const t=Ut(),n=ss();return Me(()=>{Fe(()=>t.hash,r=>{const o=document.querySelector(".sidebar");if(!o)return;const s=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${r}"]`);if(!s)return;const{top:i,height:l}=o.getBoundingClientRect(),{top:a,height:c}=s.getBoundingClientRect();ai+l&&s.scrollIntoView(!1)})}),(r,o)=>X(n).length?(j(),Z("ul",gm,[(j(!0),Z(_e,null,Ft(X(n),s=>(j(),xe(mm,{key:`${s.text}${s.link}`,item:s},null,8,["item"]))),128))])):ke("",!0)}}),ym=Se(vm,[["__file","SidebarItems.vue"]]),_m={class:"sidebar"},bm=pe({__name:"Sidebar",setup(e){return(t,n)=>(j(),Z("aside",_m,[se(ia),ye(t.$slots,"top"),se(ym),ye(t.$slots,"bottom")]))}}),wm=Se(bm,[["__file","Sidebar.vue"]]),Em=pe({__name:"Layout",setup(e){const t=mn(),n=pt(),r=Ne(),o=I(()=>n.value.navbar!==!1&&r.value.navbar!==!1),s=ss(),i=ce(!1),l=E=>{i.value=typeof E=="boolean"?E:!i.value},a={x:0,y:0},c=E=>{a.x=E.changedTouches[0].clientX,a.y=E.changedTouches[0].clientY},u=E=>{const A=E.changedTouches[0].clientX-a.x,T=E.changedTouches[0].clientY-a.y;Math.abs(A)>Math.abs(T)&&Math.abs(A)>40&&(A>0&&a.x<=80?l(!0):l(!1))},f=I(()=>[{"no-navbar":!o.value,"no-sidebar":!s.value.length,"sidebar-open":i.value},n.value.pageClass]);let d;Me(()=>{d=sn().afterEach(()=>{l(!1)})}),jr(()=>{d()});const m=Xl(),g=m.resolve,b=m.pending;return(E,A)=>(j(),Z("div",{class:Ke(["theme-container",f.value]),onTouchstart:c,onTouchend:u},[ye(E.$slots,"navbar",{},()=>[o.value?(j(),xe(Dp,{key:0,onToggleSidebar:l},{before:Le(()=>[ye(E.$slots,"navbar-before")]),after:Le(()=>[ye(E.$slots,"navbar-after")]),_:3})):ke("",!0)]),ne("div",{class:"sidebar-mask",onClick:A[0]||(A[0]=T=>l(!1))}),ye(E.$slots,"sidebar",{},()=>[se(wm,null,{top:Le(()=>[ye(E.$slots,"sidebar-top")]),bottom:Le(()=>[ye(E.$slots,"sidebar-bottom")]),_:3})]),ye(E.$slots,"page",{},()=>[X(n).home?(j(),xe(lp,{key:0})):(j(),xe(bn,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:X(g),onBeforeLeave:X(b)},{default:Le(()=>[(j(),xe(dm,{key:X(t).path},{top:Le(()=>[ye(E.$slots,"page-top")]),"content-top":Le(()=>[ye(E.$slots,"page-content-top")]),"content-bottom":Le(()=>[ye(E.$slots,"page-content-bottom")]),bottom:Le(()=>[ye(E.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),Cm=Se(Em,[["__file","Layout.vue"]]),Sm={class:"theme-container"},xm={class:"page"},Lm={class:"theme-default-content"},km=ne("h1",null,"404",-1),Am=pe({__name:"NotFound",setup(e){const t=wn(),n=Ne(),r=n.value.notFound??["Not Found"],o=()=>r[Math.floor(Math.random()*r.length)],s=n.value.home??t.value,i=n.value.backToHome??"Back to home";return(l,a)=>(j(),Z("div",Sm,[ne("main",xm,[ne("div",Lm,[km,ne("blockquote",null,Ce(o()),1),se(X(or),{to:X(s)},{default:Le(()=>[mt(Ce(X(i)),1)]),_:1},8,["to"])])])]))}}),Tm=Se(Am,[["__file","NotFound.vue"]]),Pm=ct({enhance({app:e,router:t}){uo("Badge")||e.component("Badge",_h),uo("CodeGroup")||e.component("CodeGroup",bh),uo("CodeGroupItem")||e.component("CodeGroupItem",Eh),e.component("AutoLinkExternalIcon",()=>{const r=e.component("ExternalLinkIcon");return r?oe(r):null}),e.component("NavbarSearch",()=>{const r=e.component("Docsearch")||e.component("SearchBox");return r?oe(r):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...r)=>(await Xl().wait(),n(...r))},setup(){Sh(),Ih()},layouts:{Layout:Cm,NotFound:Tm}}),Rm=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,Om=(e,t)=>t.some(n=>{if(it(n))return n===e.key;const{key:r,ctrl:o=!1,shift:s=!1,alt:i=!1}=n;return r===e.key&&o===e.ctrlKey&&s===e.shiftKey&&i===e.altKey}),Im=/[^\x00-\x7F]/,$m=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),yi=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),_i=(e,t)=>{const n=t.join(" "),r=$m(e);if(Im.test(e))return r.some(i=>n.toLowerCase().indexOf(i)>-1);const o=e.endsWith(" ");return new RegExp(r.map((i,l)=>r.length===l+1&&!o?`(?=.*\\b${yi(i)})`:`(?=.*\\b${yi(i)}\\b)`).join("")+".+","gi").test(n)},Nm=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const n=r=>{e.value&&Om(r,t.value)&&!Rm(r.target)&&(r.preventDefault(),e.value.focus())};Me(()=>{document.addEventListener("keydown",n)}),zr(()=>{document.removeEventListener("keydown",n)})},Mm=[{title:"Welcome",headers:[{level:2,title:"Prerequisites",slug:"prerequisites",link:"#prerequisites",children:[]},{level:2,title:"Useful links",slug:"useful-links",link:"#useful-links",children:[]}],path:"/",pathLocale:"/",extraFields:[]},{title:"Communicate with a REST API",headers:[{level:2,title:"Some useful concepts",slug:"some-useful-concepts",link:"#some-useful-concepts",children:[]},{level:2,title:"PW: call a REST API",slug:"pw-call-a-rest-api",link:"#pw-call-a-rest-api",children:[]}],path:"/api-communication/",pathLocale:"/",extraFields:[]},{title:"Mini project",headers:[{level:2,title:"Requirements",slug:"requirements",link:"#requirements",children:[]},{level:2,title:"Hints",slug:"hints",link:"#hints",children:[]}],path:"/mini-project/",pathLocale:"/",extraFields:[]},{title:"Locally persisting data",headers:[{level:2,title:"UserDefaults",slug:"userdefaults",link:"#userdefaults",children:[]},{level:2,title:"Codable saved in a file",slug:"codable-saved-in-a-file",link:"#codable-saved-in-a-file",children:[]},{level:2,title:"Sophisticated data persistence libraries",slug:"sophisticated-data-persistence-libraries",link:"#sophisticated-data-persistence-libraries",children:[{level:3,title:"Core Data",slug:"core-data",link:"#core-data",children:[]},{level:3,title:"Realm",slug:"realm",link:"#realm",children:[]},{level:3,title:"Firebase datastore (or any other cloud based storage)",slug:"firebase-datastore-or-any-other-cloud-based-storage",link:"#firebase-datastore-or-any-other-cloud-based-storage",children:[]}]},{level:2,title:"PW: complete the official iOS persisting data tutorial",slug:"pw-complete-the-official-ios-persisting-data-tutorial",link:"#pw-complete-the-official-ios-persisting-data-tutorial",children:[]}],path:"/persist-data/",pathLocale:"/",extraFields:[]},{title:"Presentation",headers:[{level:2,title:"Welcompe the world of iOS development",slug:"welcompe-the-world-of-ios-development",link:"#welcompe-the-world-of-ios-development",children:[]},{level:2,title:"History",slug:"history",link:"#history",children:[]},{level:2,title:"Getting started",slug:"getting-started",link:"#getting-started",children:[{level:3,title:"Create a CLI app with swift CLI",slug:"create-a-cli-app-with-swift-cli",link:"#create-a-cli-app-with-swift-cli",children:[]},{level:3,title:"Create an App using Xcode or Swift playgrounds",slug:"create-an-app-using-xcode-or-swift-playgrounds",link:"#create-an-app-using-xcode-or-swift-playgrounds",children:[]}]},{level:2,title:"Swift project managers",slug:"swift-project-managers",link:"#swift-project-managers",children:[]},{level:2,title:"Links and references",slug:"links-and-references",link:"#links-and-references",children:[]}],path:"/presentation/",pathLocale:"/",extraFields:[]},{title:"Swift (part 1)",headers:[{level:2,title:"A quick tour of some features",slug:"a-quick-tour-of-some-features",link:"#a-quick-tour-of-some-features",children:[]},{level:2,title:"Functions",slug:"functions",link:"#functions",children:[]},{level:2,title:"Optionals (aka. Null safety)",slug:"optionals-aka-null-safety",link:"#optionals-aka-null-safety",children:[]},{level:2,title:"Enumerations",slug:"enumerations",link:"#enumerations",children:[]},{level:2,title:"Error management",slug:"error-management",link:"#error-management",children:[]},{level:2,title:"Some features in bulk",slug:"some-features-in-bulk",link:"#some-features-in-bulk",children:[]},{level:2,title:"Exercises",slug:"exercises",link:"#exercises",children:[{level:3,title:"Exercise 1",slug:"exercise-1",link:"#exercise-1",children:[]},{level:3,title:"Exercise 2",slug:"exercise-2",link:"#exercise-2",children:[]},{level:3,title:"Exercise 3",slug:"exercise-3",link:"#exercise-3",children:[]}]},{level:2,title:"Sources",slug:"sources",link:"#sources",children:[]}],path:"/swift-part1/",pathLocale:"/",extraFields:[]},{title:"Swift (part 2)",headers:[{level:2,title:"Object oriented programming features",slug:"object-oriented-programming-features",link:"#object-oriented-programming-features",children:[]},{level:2,title:"Structs",slug:"structs",link:"#structs",children:[]},{level:2,title:"Opaque types",slug:"opaque-types",link:"#opaque-types",children:[]},{level:2,title:"Use structs by default",slug:"use-structs-by-default",link:"#use-structs-by-default",children:[]},{level:2,title:"Functional programming features",slug:"functional-programming-features",link:"#functional-programming-features",children:[]},{level:2,title:"Structured Concurrency",slug:"structured-concurrency",link:"#structured-concurrency",children:[]},{level:2,title:"Generics",slug:"generics",link:"#generics",children:[]},{level:2,title:"Key-paths",slug:"key-paths",link:"#key-paths",children:[]},{level:2,title:"Exercises",slug:"exercises",link:"#exercises",children:[{level:3,title:"Exercise 1",slug:"exercise-1",link:"#exercise-1",children:[]},{level:3,title:"Exercise 2",slug:"exercise-2",link:"#exercise-2",children:[]},{level:3,title:"Exercise 3",slug:"exercise-3",link:"#exercise-3",children:[]}]},{level:2,title:"Sources and more reading",slug:"sources-and-more-reading",link:"#sources-and-more-reading",children:[]}],path:"/swift-part2/",pathLocale:"/",extraFields:[]},{title:"Going further",headers:[{level:2,title:"Server side development",slug:"server-side-development",link:"#server-side-development",children:[]},{level:2,title:"Swift and SwoftUI on the browser",slug:"swift-and-swoftui-on-the-browser",link:"#swift-and-swoftui-on-the-browser",children:[]},{level:2,title:"Advanced Swift",slug:"advanced-swift",link:"#advanced-swift",children:[]},{level:2,title:"Conclusion",slug:"conclusion",link:"#conclusion",children:[]}],path:"/to-go-further/",pathLocale:"/",extraFields:[]},{title:"UI development",headers:[{level:2,title:"SwiftUI",slug:"swiftui",link:"#swiftui",children:[]},{level:2,title:"Prerequisites",slug:"prerequisites",link:"#prerequisites",children:[]},{level:2,title:"Anatomy of a basic view",slug:"anatomy-of-a-basic-view",link:"#anatomy-of-a-basic-view",children:[]},{level:2,title:"A summary of important concepts",slug:"a-summary-of-important-concepts",link:"#a-summary-of-important-concepts",children:[]},{level:2,title:"PW: complete some official SwiftUI tutorials",slug:"pw-complete-some-official-swiftui-tutorials",link:"#pw-complete-some-official-swiftui-tutorials",children:[]}],path:"/ui-development/",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]}],Hm=ce(Mm),Fm=()=>Hm,Bm=({searchIndex:e,routeLocale:t,query:n,maxSuggestions:r})=>{const o=I(()=>e.value.filter(s=>s.pathLocale===t.value));return I(()=>{const s=n.value.trim().toLowerCase();if(!s)return[];const i=[],l=(a,c)=>{_i(s,[c.title])&&i.push({link:`${a.path}#${c.slug}`,title:a.title,header:c.title});for(const u of c.children){if(i.length>=r.value)return;l(a,u)}};for(const a of o.value){if(i.length>=r.value)break;if(_i(s,[a.title,...a.extraFields])){i.push({link:a.path,title:a.title});continue}for(const c of a.headers){if(i.length>=r.value)break;l(a,c)}}return i})},Dm=e=>{const t=ce(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},zm=pe({name:"SearchBox",props:{locales:{type:Object,default:()=>({})},hotKeys:{type:Array,default:()=>[]},maxSuggestions:{type:Number,default:5}},setup(e){const{locales:t,hotKeys:n,maxSuggestions:r}=Hr(e),o=sn(),s=wn(),i=Fm(),l=ce(null),a=ce(!1),c=ce(""),u=I(()=>t.value[s.value]??{}),f=Bm({searchIndex:i,routeLocale:s,query:c,maxSuggestions:r}),{focusIndex:d,focusNext:m,focusPrev:g}=Dm(f);Nm({input:l,hotKeys:n});const b=I(()=>a.value&&!!f.value.length),E=()=>{b.value&&g()},A=()=>{b.value&&m()},T=v=>{if(!b.value)return;const C=f.value[v];C&&o.push(C.link).then(()=>{c.value="",d.value=0})};return()=>oe("form",{class:"search-box",role:"search"},[oe("input",{ref:l,type:"search",placeholder:u.value.placeholder,autocomplete:"off",spellcheck:!1,value:c.value,onFocus:()=>a.value=!0,onBlur:()=>a.value=!1,onInput:v=>c.value=v.target.value,onKeydown:v=>{switch(v.key){case"ArrowUp":{E();break}case"ArrowDown":{A();break}case"Enter":{v.preventDefault(),T(d.value);break}}}}),b.value&&oe("ul",{class:"suggestions",onMouseleave:()=>d.value=-1},f.value.map(({link:v,title:C,header:B},O)=>oe("li",{class:["suggestion",{focus:d.value===O}],onMouseenter:()=>d.value=O,onMousedown:()=>T(O)},oe("a",{href:v,onClick:H=>H.preventDefault()},[oe("span",{class:"page-title"},C),B&&oe("span",{class:"page-header"},`> ${B}`)]))))])}});var jm=["s","/"],Um={"/":{placeholder:"Search"},"/fr/":{placeholder:"Rechercher"}};const Wm=Um,Vm=jm,Km=5,qm=ct({enhance({app:e}){e.component("SearchBox",t=>oe(zm,{locales:Wm,hotKeys:Vm,maxSuggestions:Km,...t}))}});function Gm(e){return{all:e=e||new Map,on:function(t,n){var r=e.get(t);r?r.push(n):e.set(t,[n])},off:function(t,n){var r=e.get(t);r&&(n?r.splice(r.indexOf(n)>>>0,1):e.set(t,[]))},emit:function(t,n){var r=e.get(t);r&&r.slice().map(function(o){o(n)}),(r=e.get("*"))&&r.slice().map(function(o){o(t,n)})}}}const Ym=()=>{navigator.serviceWorker.getRegistration().then(e=>{e&&e.active&&(e==null||e.addEventListener("updatefound",()=>{window.location.reload(!0)}))})},Jm=async(e,t={},n=!0)=>{const{register:r}=await rt(()=>import("./index-DTEEl-sV.js"),[]);r(e,{ready(o){var s;n&&console.info("[Service Worker]: active"),(s=t.ready)==null||s.call(t,o)},registered(o){var s;n&&console.log("[Service Worker]: registered"),(s=t.registered)==null||s.call(t,o)},cached(o){var s;n&&console.log("[Service Worker]: cached"),(s=t.cached)==null||s.call(t,o)},async updatefound(o){var s;await navigator.serviceWorker.getRegistration()&&(n&&console.log("[Service Worker]: update found"),(s=t.updatefound)==null||s.call(t,o))},updated(o){var s;n&&console.log("[Service Worker]: updated"),(s=t.updated)==null||s.call(t,o)},offline(){var o;n&&console.log("[Service Worker]: offline"),(o=t.offline)==null||o.call(t)},error(o){var s;n&&console.error("[Service Worker]: ",o),(s=t.error)==null||s.call(t,o)}})},Xm=e=>{const t=e.waiting;if(!t)return;const n=new MessageChannel;t.postMessage({type:"SKIP_WAITING"},[n.port2])},la=Symbol(""),Qm=()=>{const e=ze(la);if(!e)throw new Error("usePwaEvent() is called without provider.");return e},Zm=async(e,t)=>Jm(rr(e),{ready(n){t.emit("ready",n)},registered(n){t.emit("registered",n)},cached(n){t.emit("cached",n)},updatefound(n){t.emit("updatefound",n)},updated(n){const r="service-worker-version",o=Number(localStorage.getItem(r)||0);localStorage.setItem(r,(o+1).toString()),localStorage.removeItem("manifest"),t.emit("updated",n)},offline(){t.emit("offline")},error(n){t.emit("error",n)}}),eg=(e,t=!1)=>{const n=Gm();Mt(la,n),Me(async()=>{var o;let r=!1;(o=navigator.serviceWorker)!=null&&o.controller&&navigator.serviceWorker.addEventListener("controllerchange",()=>{r||(r=!0,window.location.reload())}),t&&Ym(),await Zm(e,n)})},tg=()=>{Me(()=>{if(window.matchMedia("(display-mode: standalone)").matches){const t=document.head.querySelector('meta[name="viewport"]');if(t){t.setAttribute("content","width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover");return}const n=document.createElement("meta");n.name="viewport",n.content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover",document.head.appendChild(n)}})},aa=({name:e="",color:t="currentColor"},{slots:n})=>{var r;return oe("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(r=n.default)==null?void 0:r.call(n))};aa.displayName="SVGWrapper";const ca=()=>oe(aa,{name:"update"},()=>oe("path",{d:"M949.949 146.25v255.826c0 21.981-13.989 35.97-35.97 35.97H658.154c-13.988 0-25.983-7.992-33.973-21.981-5.997-13.989-4-27.977 7.991-39.97l79.942-77.946c-55.954-51.973-121.918-77.955-199.863-77.955-37.975 0-75.95 8.002-113.924 21.99-37.975 15.985-67.948 37.976-91.934 63.957-25.982 23.987-47.973 53.96-63.957 91.934-29.983 73.955-29.983 153.895 0 227.85 15.984 37.976 37.975 67.947 63.957 91.934 23.986 25.982 53.959 47.973 91.934 63.956 37.974 13.989 75.95 21.991 113.924 21.991 45.967 0 87.942-9.998 127.913-29.982 41.976-17.99 75.951-45.967 101.931-83.943 7.993-4 11.994-5.995 13.989-5.995 5.997 0 9.998 1.994 13.988 5.995l77.958 77.946c3.989 4 5.986 7.993 5.986 11.994 0 1.994-1.996 5.995-3.99 11.994-43.973 51.962-93.941 91.934-151.9 117.914-53.958 25.983-115.92 39.972-185.874 39.972-61.961 0-119.921-11.984-169.89-33.973-57.96-25.985-105.923-57.963-139.896-93.943-35.98-33.972-67.958-81.936-93.94-139.897-45.967-101.93-45.967-237.846 0-339.777 25.982-57.96 57.96-105.923 93.94-139.896 33.973-35.98 81.936-67.958 139.896-93.94 49.968-21.99 107.928-33.974 169.89-33.974 55.963 0 109.923 9.988 161.885 29.973 53.97 21.99 101.933 51.963 139.908 89.938l73.954-73.944c9.987-9.998 23.987-13.988 39.971-8.002 13.988 8.002 21.98 19.995 21.98 33.984z"}));ca.displayName="UpdateIcon";const ng=pe({name:"PwaReadyPopup",props:{locales:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const n=ns(e.locales),r=on(),o=I(()=>!!r.value),s=()=>{r.value&&(Xm(r.value),r.value=void 0)};return Me(()=>{Qm().on("updated",l=>{l&&(r.value=l)})}),()=>oe(bn,{name:"popup"},()=>{var i;return((i=t.default)==null?void 0:i.call(t,{isReady:o.value,reload:s}))||(o.value?oe("button",{type:"button",class:"sw-update-popup",tabindex:0,onClick:()=>s()},[n.value.update,oe("span",{class:"icon-wrapper"},oe(ca))]):null)})}});var rg={"/":{install:"Install",iOSInstall:"Tap the share button and then 'Add to Home Screen'",cancel:"Cancel",close:"Close",prevImage:"Previous Image",nextImage:"Next Image",desc:"Description",feature:"Key Features",explain:"This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system. ",hint:"New content found.",update:"New content is available."}};const og=rg,sg=()=>oe(ng,{locales:og}),ig=ct({setup:()=>{eg("service-worker.js",!1),tg()},rootComponents:[sg]});/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */var Gt=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},r=window.Promise||function(w){function M(){}w(M,M)},o=function(w){var M=w.target;if(M===G){g();return}v.indexOf(M)!==-1&&b({target:M})},s=function(){if(!(B||!y.original)){var w=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(O-w)>H.scrollOffset&&setTimeout(g,150)}},i=function(w){var M=w.key||w.keyCode;(M==="Escape"||M==="Esc"||M===27)&&g()},l=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=w;if(w.background&&(G.style.background=w.background),w.container&&w.container instanceof Object&&(M.container=Gt({},H.container,w.container)),w.template){var te=wr(w.template)?w.template:document.querySelector(w.template);M.template=te}return H=Gt({},H,M),v.forEach(function(ie){ie.dispatchEvent(an("medium-zoom:update",{detail:{zoom:L}}))}),L},a=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Gt({},H,w))},c=function(){for(var w=arguments.length,M=Array(w),te=0;te0?M.reduce(function(R,Y){return[].concat(R,wi(Y))},[]):v;return ie.forEach(function(R){R.classList.remove("medium-zoom-image"),R.dispatchEvent(an("medium-zoom:detach",{detail:{zoom:L}}))}),v=v.filter(function(R){return ie.indexOf(R)===-1}),L},f=function(w,M){var te=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(ie){ie.addEventListener("medium-zoom:"+w,M,te)}),C.push({type:"medium-zoom:"+w,listener:M,options:te}),L},d=function(w,M){var te=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(ie){ie.removeEventListener("medium-zoom:"+w,M,te)}),C=C.filter(function(ie){return!(ie.type==="medium-zoom:"+w&&ie.listener.toString()===M.toString())}),L},m=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=w.target,te=function(){var R={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},Y=void 0,V=void 0;if(H.container)if(H.container instanceof Object)R=Gt({},R,H.container),Y=R.width-R.left-R.right-H.margin*2,V=R.height-R.top-R.bottom-H.margin*2;else{var Re=wr(H.container)?H.container:document.querySelector(H.container),He=Re.getBoundingClientRect(),We=He.width,Be=He.height,Ct=He.left,St=He.top;R=Gt({},R,{width:We,height:Be,left:Ct,top:St})}Y=Y||R.width-H.margin*2,V=V||R.height-H.margin*2;var ut=y.zoomedHd||y.original,Oe=bi(ut)?Y:ut.naturalWidth||Y,x=bi(ut)?V:ut.naturalHeight||V,U=ut.getBoundingClientRect(),D=U.top,q=U.left,ue=U.width,ge=U.height,h=Math.min(Math.max(ue,Oe),Y)/ue,p=Math.min(Math.max(ge,x),V)/ge,_=Math.min(h,p),k=(-q+(Y-ue)/2+H.margin+R.left)/_,S=(-D+(V-ge)/2+H.margin+R.top)/_,$="scale("+_+") translate3d("+k+"px, "+S+"px, 0)";y.zoomed.style.transform=$,y.zoomedHd&&(y.zoomedHd.style.transform=$)};return new r(function(ie){if(M&&v.indexOf(M)===-1){ie(L);return}var R=function We(){B=!1,y.zoomed.removeEventListener("transitionend",We),y.original.dispatchEvent(an("medium-zoom:opened",{detail:{zoom:L}})),ie(L)};if(y.zoomed){ie(L);return}if(M)y.original=M;else if(v.length>0){var Y=v;y.original=Y[0]}else{ie(L);return}if(y.original.dispatchEvent(an("medium-zoom:open",{detail:{zoom:L}})),O=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,B=!0,y.zoomed=cg(y.original),document.body.appendChild(G),H.template){var V=wr(H.template)?H.template:document.querySelector(H.template);y.template=document.createElement("div"),y.template.appendChild(V.content.cloneNode(!0)),document.body.appendChild(y.template)}if(y.original.parentElement&&y.original.parentElement.tagName==="PICTURE"&&y.original.currentSrc&&(y.zoomed.src=y.original.currentSrc),document.body.appendChild(y.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),y.original.classList.add("medium-zoom-image--hidden"),y.zoomed.classList.add("medium-zoom-image--opened"),y.zoomed.addEventListener("click",g),y.zoomed.addEventListener("transitionend",R),y.original.getAttribute("data-zoom-src")){y.zoomedHd=y.zoomed.cloneNode(),y.zoomedHd.removeAttribute("srcset"),y.zoomedHd.removeAttribute("sizes"),y.zoomedHd.removeAttribute("loading"),y.zoomedHd.src=y.zoomed.getAttribute("data-zoom-src"),y.zoomedHd.onerror=function(){clearInterval(Re),console.warn("Unable to reach the zoom image target "+y.zoomedHd.src),y.zoomedHd=null,te()};var Re=setInterval(function(){y.zoomedHd.complete&&(clearInterval(Re),y.zoomedHd.classList.add("medium-zoom-image--opened"),y.zoomedHd.addEventListener("click",g),document.body.appendChild(y.zoomedHd),te())},10)}else if(y.original.hasAttribute("srcset")){y.zoomedHd=y.zoomed.cloneNode(),y.zoomedHd.removeAttribute("sizes"),y.zoomedHd.removeAttribute("loading");var He=y.zoomedHd.addEventListener("load",function(){y.zoomedHd.removeEventListener("load",He),y.zoomedHd.classList.add("medium-zoom-image--opened"),y.zoomedHd.addEventListener("click",g),document.body.appendChild(y.zoomedHd),te()})}else te()})},g=function(){return new r(function(w){if(B||!y.original){w(L);return}var M=function te(){y.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(y.zoomed),y.zoomedHd&&document.body.removeChild(y.zoomedHd),document.body.removeChild(G),y.zoomed.classList.remove("medium-zoom-image--opened"),y.template&&document.body.removeChild(y.template),B=!1,y.zoomed.removeEventListener("transitionend",te),y.original.dispatchEvent(an("medium-zoom:closed",{detail:{zoom:L}})),y.original=null,y.zoomed=null,y.zoomedHd=null,y.template=null,w(L)};B=!0,document.body.classList.remove("medium-zoom--opened"),y.zoomed.style.transform="",y.zoomedHd&&(y.zoomedHd.style.transform=""),y.template&&(y.template.style.transition="opacity 150ms",y.template.style.opacity=0),y.original.dispatchEvent(an("medium-zoom:close",{detail:{zoom:L}})),y.zoomed.addEventListener("transitionend",M)})},b=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=w.target;return y.original?g():m({target:M})},E=function(){return H},A=function(){return v},T=function(){return y.original},v=[],C=[],B=!1,O=0,H=n,y={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?H=t:(t||typeof t=="string")&&c(t),H=Gt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},H);var G=ag(H.background);document.addEventListener("click",o),document.addEventListener("keyup",i),document.addEventListener("scroll",s),window.addEventListener("resize",g);var L={open:m,close:g,toggle:b,update:l,clone:a,attach:c,detach:u,on:f,off:d,getOptions:E,getImages:A,getZoomedImage:T};return L};function fg(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||typeof document>"u")){var r=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",n==="top"&&r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var dg=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";fg(dg);const hg=Symbol("mediumZoom");var pg={};const mg=":not(a) > img",gg=pg,vg=500,yg=ct({enhance({app:e,router:t}){const n=ug(gg);n.refresh=(r=mg)=>{n.detach(),n.attach(r)},e.provide(hg,n),t.afterEach(()=>{Kl(vg).then(()=>n.refresh())})}}),_g={},bg=ct({enhance:({app:e})=>{},setup:()=>{}}),yr=[Dd,Kd,rh,ah,dh,vh,Pm,qm,ig,yg,_g,bg],wg=JSON.parse('{"base":"/ios-training/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"icon","href":"/ios-training/favicon.ico"}],["link",{"rel":"manifest","href":"/ios-training/manifest.webmanifest"}],["meta",{"name":"theme-color","content":"#2176d6"}]],"locales":{"/":{"lang":"en-US","title":"iOS Training","description":"iOS training docs"}}}');var An=on(wg),Eg=Tf,Cg=()=>{const e=ed({history:Eg(xl("/ios-training/")),routes:[{name:"vuepress-route",path:"/:catchAll(.*)",components:{}}],scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{if(t.path!==n.path||n===_t){const r=Kn(t.path);if(r.path!==t.path)return r.path;const o=await r.loader();t.meta={...r.meta,_pageChunk:o}}else t.path===n.path&&(t.meta=n.meta)}),e},Sg=e=>{e.component("ClientOnly",Zo),e.component("Content",ad),e.component("RouteLink",or)},xg=(e,t,n)=>{const r=I(()=>t.currentRoute.value.path),o=Xa((E,A)=>({get(){return E(),t.currentRoute.value.meta._pageChunk},set(T){t.currentRoute.value.meta._pageChunk=T,A()}})),s=I(()=>qt.resolveLayouts(n)),i=I(()=>qt.resolveRouteLocale(An.value.locales,r.value)),l=I(()=>qt.resolveSiteLocaleData(An.value,i.value)),a=I(()=>o.value.comp),c=I(()=>o.value.data),u=I(()=>c.value.frontmatter),f=I(()=>qt.resolvePageHeadTitle(c.value,l.value)),d=I(()=>qt.resolvePageHead(f.value,u.value,l.value)),m=I(()=>qt.resolvePageLang(c.value,l.value)),g=I(()=>qt.resolvePageLayout(c.value,s.value)),b={layouts:s,pageData:c,pageComponent:a,pageFrontmatter:u,pageHead:d,pageHeadTitle:f,pageLang:m,pageLayout:g,redirects:To,routeLocale:i,routePath:r,routes:Vn,siteData:An,siteLocaleData:l};return e.provide(Xo,b),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>u.value},$head:{get:()=>d.value},$headTitle:{get:()=>f.value},$lang:{get:()=>m.value},$page:{get:()=>c.value},$routeLocale:{get:()=>i.value},$site:{get:()=>An.value},$siteLocale:{get:()=>l.value},$withBase:{get:()=>rr}}),b},Lg=()=>{const e=rd(),t=od();let n=[];const r=()=>{e.value.forEach(i=>{const l=kg(i);l&&n.push(l)})},o=()=>{const i=[];return e.value.forEach(l=>{const a=Ag(l);a&&i.push(a)}),i},s=()=>{document.documentElement.lang=t.value;const i=o();n.forEach((l,a)=>{const c=i.findIndex(u=>l.isEqualNode(u));c===-1?(l.remove(),delete n[a]):i.splice(c,1)}),i.forEach(l=>document.head.appendChild(l)),n=[...n.filter(l=>!!l),...i]};Mt(ld,s),Me(()=>{r(),Fe(e,s,{immediate:!1})})},kg=([e,t,n=""])=>{const r=Object.entries(t).map(([l,a])=>it(a)?`[${l}=${JSON.stringify(a)}]`:a===!0?`[${l}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(l=>l.innerText===n)||null},Ag=([e,t,n])=>{if(!it(e))return null;const r=document.createElement(e);return Go(t)&&Object.entries(t).forEach(([o,s])=>{it(s)?r.setAttribute(o,s):s===!0&&r.setAttribute(o,"")}),it(n)&&r.appendChild(document.createTextNode(n)),r},Tg=Du,Pg=async()=>{var n;const e=Tg({name:"Vuepress",setup(){var s;Lg();for(const i of yr)(s=i.setup)==null||s.call(i);const r=yr.flatMap(({rootComponents:i=[]})=>i.map(l=>oe(l))),o=sd();return()=>[oe(o.value),r]}}),t=Cg();Sg(e),xg(e,t,yr);for(const r of yr)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:An}));return e.use(t),{app:e,router:t}};Pg().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{Se as _,ne as a,mt as b,Z as c,Pg as createVueApp,se as d,eu as e,j as o,tn as r}; diff --git a/assets/hello-swiftui-DCdT_J-Q.png b/assets/hello-swiftui-DCdT_J-Q.png new file mode 100644 index 0000000..3857dd6 Binary files /dev/null and b/assets/hello-swiftui-DCdT_J-Q.png differ diff --git a/assets/index-DTEEl-sV.js b/assets/index-DTEEl-sV.js new file mode 100644 index 0000000..6932654 --- /dev/null +++ b/assets/index-DTEEl-sV.js @@ -0,0 +1 @@ +var v=function(){return!!(window.location.hostname==="localhost"||window.location.hostname==="[::1]"||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/))},c;typeof window<"u"&&(typeof Promise<"u"?c=new Promise(function(t){return window.addEventListener("load",t)}):c={then:function(t){return window.addEventListener("load",t)}});function s(t,n){n===void 0&&(n={});var i=n.registrationOptions;i===void 0&&(i={}),delete n.registrationOptions;var e=function(r){for(var f=[],a=arguments.length-1;a-- >0;)f[a]=arguments[a+1];n&&n[r]&&n[r].apply(n,f)};"serviceWorker"in navigator&&c.then(function(){v()?(l(t,e,i),navigator.serviceWorker.ready.then(function(r){e("ready",r)}).catch(function(r){return o(e,r)})):(u(t,e,i),navigator.serviceWorker.ready.then(function(r){e("ready",r)}).catch(function(r){return o(e,r)}))})}function o(t,n){navigator.onLine||t("offline"),t("error",n)}function u(t,n,i){navigator.serviceWorker.register(t,i).then(function(e){if(n("registered",e),e.waiting){n("updated",e);return}e.onupdatefound=function(){n("updatefound",e);var r=e.installing;r.onstatechange=function(){r.state==="installed"&&(navigator.serviceWorker.controller?n("updated",e):n("cached",e))}}}).catch(function(e){return o(n,e)})}function l(t,n,i){fetch(t).then(function(e){e.status===404?(n("error",new Error("Service worker not found at "+t)),d()):e.headers.get("content-type").indexOf("javascript")===-1?(n("error",new Error("Expected "+t+" to have javascript content-type, but received "+e.headers.get("content-type"))),d()):u(t,n,i)}).catch(function(e){return o(n,e)})}function d(){"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(t){t.unregister()}).catch(function(t){return o(emit,t)})}export{s as register,d as unregister}; diff --git a/assets/index.html-5n7MuywO.js b/assets/index.html-5n7MuywO.js new file mode 100644 index 0000000..4faee47 --- /dev/null +++ b/assets/index.html-5n7MuywO.js @@ -0,0 +1,39 @@ +import{_ as o,r as i,o as r,c as l,a as e,b as n,d as t,e as a}from"./app-Bbun9eEO.js";const c={},p=a('

Swift (part 2)

Estimated time

1/2 day

Object oriented programming features

Swift supports most Object Oriented Programming features:

',4),u=e("li",null,[n("Classes that can be instantiated into objects. "),e("ul",null,[e("li",null,"Constructors and destructors are called initializers and deinitializers respectively.")])],-1),d={href:"https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html",target:"_blank",rel:"noopener noreferrer"},h=e("li",null,"Simple inheritance of classes. Multiple inheritance of classes and is not supported.",-1),f=e("li",null,"Inheritance allows one class to use the characteristics of another.",-1),m={href:"https://levelup.gitconnected.com/polymorphism-in-swift",target:"_blank",rel:"noopener noreferrer"},b={href:"https://www.avanderlee.com/swift/composition-inheritance-code-architecture/",target:"_blank",rel:"noopener noreferrer"},k=e("li",null,"Static methods and properties are supported.",-1),g=e("li",null,"Generic types are supported",-1),w=e("em",null,"Protocols",-1),_=e("em",null,"Interfaces",-1),v=e("li",null,"Classes and structs can conform to multiple protocols.",-1),y={href:"https://www.hackingwithswift.com/articles/74/understanding-protocol-associated-types-and-their-constraints",target:"_blank",rel:"noopener noreferrer"},x=e("li",null,[n("They are used a lot by swift developers to the point that there is a programming technique called "),e("strong",null,"Protocol oriented programming"),n(".")],-1),S=e("p",null,"Here are some additional features:",-1),T=e("ul",null,[e("li",null,[n("Extensions allow to add functions and conform to additional protocols outside of the original class, struct or protocol declaration. This has many uses that simplify our code and here are some examples. "),e("ul",null,[e("li",null,"They can add methods to classes from any library that we can use."),e("li",null,"They can define default implementations in protocols.")])]),e("li",null,"Abstract classes are not available")],-1),q={href:"https://swiftfiddle.com/05f4d4d3c8235299a875e08dcb3992f8",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"In additions to classes, structs in swift are powerful and provide similar features than classes with some exceptions.",-1),E=e("h2",{id:"structs",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#structs"},[e("span",null,"Structs")])],-1),P={href:"https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html",target:"_blank",rel:"noopener noreferrer"},A={href:"https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html",target:"_blank",rel:"noopener noreferrer"},F=e("ul",null,[e("li",null,"Inheritance."),e("li",null,"Type casting (enables you to check and interpret the type of a class instance at runtime)."),e("li",null,"Deinitializers."),e("li",null,"Reference counting allows more than one reference to a class instance (similar to pointers but much less complex to use).")],-1),U={href:"https://swiftfiddle.com/d72ea73dcbae5cc25908c56bdabcf877",target:"_blank",rel:"noopener noreferrer"},C=a(`

Opaque types

This feature seems advanced to understand but since it's used a lot in SwiftUI, let's explore a simple explanation and we'll provide some links to study it further.

In a base level, opaque types allow to return Protocols while keeping the concrete type information known by the compiler. It is enabled by prefixing the type with the some keyword.

Opaque types allow to keep the benefits of abstracting the code on a developer level while maintaining the performance and optimization benefits of concrete typing. In addition to that, they allow the compiler to better handle some cases such as Self or associated type requirements. Please note that explaining all the features that opaque types bring to the code is an advanced topic. For more information and details, please read the articles mentioned in the Sources and more reading section.

For this training, we'll assume that opaque help types the compiler perform better optimizations with protocols, are used in many places in SwiftUI and allow to improve our code in some cases. We'll show below a simple use case where we can define a method that returns an Equatable.

// Source: https://www.educative.io/answers/what-is-opaque-type-in-swift
+
+// create a function that returns some Equatable
+// The compiler fails is the return type is just "Equatable"
+func makeInteger() -> some Equatable{
+  Int.random(in: 0...10)
+}
+
+let firstInteger = makeInteger()
+let secondInteger = makeInteger()
+
+// this returns "false" because they are of the same concrete type else, Xcode will scream at us.
+print(firstInteger == secondInteger)
+
+func makeString() -> some Equatable{
+  "A String"
+}
+let firstString = makeString()
+
+// Compiler error because the concrete type is not the same.
+print(firstInteger == firstString)
+
`,6),j={href:"https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md",target:"_blank",rel:"noopener noreferrer"},G={href:"https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md",target:"_blank",rel:"noopener noreferrer"},O=e("h2",{id:"use-structs-by-default",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#use-structs-by-default"},[e("span",null,"Use structs by default")])],-1),W={href:"https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes",target:"_blank",rel:"noopener noreferrer"},z=e("ul",null,[e("li",null,"Use structures by default."),e("li",null,"Use classes when you need Objective-C interoperability."),e("li",null,"Use classes when you need to control the identity of the data you’re modeling."),e("li",null,"Use structures along with protocols to adopt behavior by sharing implementations.")],-1),R=e("p",null,"We note that structures are the default choice mostly because they are value types. This makes the code more predictable because changes cannot come from a parent call. Another advantage of structs is that they are more friendly with functional programming. We'll talk about functional programming in the next section.",-1),L=e("h2",{id:"functional-programming-features",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#functional-programming-features"},[e("span",null,"Functional programming features")])],-1),N={href:"https://flexiple.com/ios/introduction-to-functional-programming-using-swift/",target:"_blank",rel:"noopener noreferrer"},B=e("p",null,"Pure functions are functions that do not have side effects and will thus return always the same output given the same input. Swift allows to create pure functions but does not provide compile time guarantees that a function is pure.",-1),D=e("code",null,"let",-1),K={href:"https://stackoverflow.com/a/24232845",target:"_blank",rel:"noopener noreferrer"},M={href:"https://blog.ndepend.com/declarative-programming-depth/",target:"_blank",rel:"noopener noreferrer"},V={href:"https://swiftfiddle.com/4cebea7bfea3d58600df30f1af325663",target:"_blank",rel:"noopener noreferrer"},H=e("p",null,"Swift has many more features and provides a rich standard library. We'll explore them as needed in the next sections. For now, let's create some UIs in the next chapter.",-1),J=e("h2",{id:"structured-concurrency",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#structured-concurrency"},[e("span",null,"Structured Concurrency")])],-1),Q=e("li",null,"Swift supports writing concurrent code in a structured way.",-1),X=e("li",null,[n("Concurrency means that we execute multiple "),e("strong",null,"tasks"),n(" at the same time. For example, update the UI while the app performs an HTTP request to the server.")],-1),Y=e("li",null,[n("In Swift, we can create concurrent Tasks with the "),e("code",null,"Task"),n(", "),e("code",null,"TaskGroup"),n(" types.")],-1),Z={href:"https://swiftfiddle.com/ce6995e6f1cd9c0fb226df7995b546a5",target:"_blank",rel:"noopener noreferrer"},$={href:"https://oleb.net/2021/structured-concurrency",target:"_blank",rel:"noopener noreferrer"},ee=a("
  • Structured concurrency means that we write concurrent code using the usual control flow structures (as opposed to callback-based concurrent code)
  • In Swift is possible through the async and await keywords.
  • When we await a Task, the control flow will continue when it end without blocking the Task or TaskGroup on which it is launched.
  • A function that has uses the await keyword must be declared as async
",1),ne=e("li",null,[n("To summarize "),e("code",null,"async and await"),n(" + "),e("code",null,"Task"),n(" and "),e("code",null,"TaskGroup"),n(" = "),e("strong",null,"Structured Concurrency")],-1),se=e("li",null,"Continuations allow to convert callback code into async/await",-1),te={href:"https://swiftfiddle.com/c34b73f3b260192f63bd8159b9853986",target:"_blank",rel:"noopener noreferrer"},ae={href:"https://swiftfiddle.com/93183f842d0d02756b3e911e9ddc24b8",target:"_blank",rel:"noopener noreferrer"},oe={href:"https://swiftfiddle.com/d5c9039422d60ce14f307623a3d9107e",target:"_blank",rel:"noopener noreferrer"},ie={href:"https://swiftfiddle.com/d62a161afcd623615a27fb09a8b2dc5c",target:"_blank",rel:"noopener noreferrer"},re=a(`

Structured Concurrency in Playground Book

// Reference: https://stackoverflow.com/a/24066317
+import PlaygroundSupport
+
+//Playground does not stop at the end of the code
+PlaygroundPage.current.needsIndefiniteExecution = true
+
+func sampleFunc() async {
+  print("sampleFunc")
+  try? await Task.sleep(until: .now + .seconds(2))
+}
+
+Task {
+    await sampleFunc()
+    print("done")
+    // End the playground
+    PlaygroundPage.current.finishExecution()
+}
+

Generics

  • Generics allow to pass a type as a parameter to a class, struct, enum or function.
  • A type parameter can be declares with <T> where T is the type parameter.
  • Examples
    • func printArray<T>(array: [T]) { for item in array { print(item) } }
  • Swift can infer the type of the parameter if it is not provided and if it's not ambiguous.
`,3),le={href:"https://swiftfiddle.com/c2619f36b41875606075e1f4baf2b93a",target:"_blank",rel:"noopener noreferrer"},ce=e("h2",{id:"key-paths",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#key-paths"},[e("span",null,"Key-paths")])],-1),pe={href:"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/expressions/#Key-Path-Expression",target:"_blank",rel:"noopener noreferrer"},ue=e("li",null,[n("They are created with the "),e("code",null,"\\.propertyName"),n(" syntax.")],-1),de=e("li",null,"They are often used to sort, filter, group and map collections and in SwiftUI to bind properties to UI elements.",-1),he={href:"https://swiftfiddle.com/9a220108db372f3c2063d4d7275001a9",target:"_blank",rel:"noopener noreferrer"},fe=e("h2",{id:"exercises",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercises"},[e("span",null,"Exercises")])],-1),me=e("h3",{id:"exercise-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-1"},[e("span",null,"Exercise 1")])],-1),be={href:"https://swiftfiddle.com/5d65286d3db0ccf08f7ca3bf1cef31fe",target:"_blank",rel:"noopener noreferrer"},ke={class:"custom-container details"},ge=e("summary",null,"Please open to see the solution(s)",-1),we={href:"https://swiftfiddle.com/41469e54bc7c025b003341a0e96f16a3",target:"_blank",rel:"noopener noreferrer"},_e=e("h3",{id:"exercise-2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-2"},[e("span",null,"Exercise 2")])],-1),ve={href:"https://swiftfiddle.com/43fc226645abb5457d26c7176fb6009d",target:"_blank",rel:"noopener noreferrer"},ye={class:"custom-container details"},xe=e("summary",null,"Please open to see the solution(s)",-1),Se={href:"https://swiftfiddle.com/a1227e17989ad80da5137a31aa6dfbeb",target:"_blank",rel:"noopener noreferrer"},Te=e("h3",{id:"exercise-3",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-3"},[e("span",null,"Exercise 3")])],-1),qe={href:"https://swiftfiddle.com/d225e7e3b061035b6a1987c3cf2fb3d5",target:"_blank",rel:"noopener noreferrer"},Ie={class:"custom-container details"},Ee=e("summary",null,"Please open to see the solution(s)",-1),Pe={href:"https://swiftfiddle.com/b45e785e8d832058e394f179782b214c",target:"_blank",rel:"noopener noreferrer"},Ae={href:"https://swiftfiddle.com/6aac1fb721c00c565509dded883f7481",target:"_blank",rel:"noopener noreferrer"},Fe=e("h2",{id:"sources-and-more-reading",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sources-and-more-reading"},[e("span",null,"Sources and more reading")])],-1),Ue={href:"https://docs.swift.org",target:"_blank",rel:"noopener noreferrer"},Ce={href:"https://tanaschita.com/20220206-understanding-opaque-types-in-swift/",target:"_blank",rel:"noopener noreferrer"},je={href:"https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md",target:"_blank",rel:"noopener noreferrer"},Ge={href:"https://async-await-in-swift.netlify.app/",target:"_blank",rel:"noopener noreferrer"},Oe={href:"https://github.com/SwiftFiddle/swiftfiddle-web/discussions/101",target:"_blank",rel:"noopener noreferrer"};function We(ze,Re){const s=i("ExternalLinkIcon");return r(),l("div",null,[p,e("ul",null,[u,e("li",null,[n("Encapsulation and "),e("a",d,[n("4 access levels"),t(s)]),n(" that range from private to public")]),h,f,e("li",null,[n("Method overriding and "),e("a",m,[n("polymorphism"),t(s)]),n(" and access control.")]),e("li",null,[n("Overloading of operators and functions, "),e("a",b,[n("composition"),t(s)]),n(".")]),k,g,e("li",null,[w,n(" which are the equivalent of "),_,n(". "),e("ul",null,[v,e("li",null,[n("Protocols can have "),e("a",y,[n("associated types"),t(s)]),n(" which is similar to generic types.")]),x])])]),S,T,e("p",null,[e("a",q,[n("this code"),t(s)]),n(" illustrates some of the above features.")]),I,E,e("p",null,[n("In Swift, structs have many similar features with classes. They support properties, methods, "),e("a",P,[n("subscripts"),t(s)]),n(", initializers, extensions and conforming to protocols. The features that are only available in classes "),e("a",A,[n("are as follows"),t(s)]),n(":")]),F,e("p",null,[e("a",U,[n("this code"),t(s)]),n(" sample shows how to use structs with protocols.")]),C,e("p",null,[n("As of Swift 5.1 "),e("a",j,[n("opaque types are only available for return values"),t(s)]),n(". As of Swift 5.7 "),e("a",G,[n("opaque arguments have been implemented"),t(s)])]),O,e("p",null,[n("As surprising as it seems, Apple recommends using "),e("a",W,[n("structs by default instead of classes"),t(s)]),n(". More precisely, when we want to add a new data type, we should not assume that it should be a class by default and check if a structure is more relevant. Apple provides the following recommendations:")]),z,R,L,e("p",null,[n("Functional programming revolves around "),e("a",N,[n("three main concepts"),t(s)]),n(": pure functions, immutable objects and declarative programming.")]),B,e("p",null,[n("Immutable objects can be created using classes or structs with constant properties (declared with "),D,n("). As mentioned above, structs are recommended by default "),e("a",K,[n("and here are other good reasons"),t(s)]),n(". One of the most notable ones is that since structs are passed around by value, thus they help us avoiding side effects.")]),e("p",null,[n("Declarative programming can be easily explained as a way of programming that is centered "),e("a",M,[n("around telling what to do and not how to do it"),t(s)]),n(". This allows to obtain a clearer and more maintainable code than traditional imperative programming. For example, when we want to filter a table, a for loop is not declarative (we say imperative in this case) while the WHERE SQL syntax is considered declarative. Declarative programing is possible in Swift through chaining functions and passing functions as arguments. Indeed, as we have seen earlier, Swift has 1st class support for functions. In addition to that, we can find declarative APIs in the standard Swift library and in Swift UI. The latter will be explored in a different chapter. For now, let's illustrate "),e("a",V,[n("with this code"),t(s)]),n(" how to process a list of strings using only declarative APIs provided by Swift.")]),H,J,e("ul",null,[Q,X,Y,e("li",null,[n("Without Structured concurrency, we would use complex concepts to such as callbacks which make code less readable (have you lived the "),e("a",Z,[n("callback hell"),t(s)]),n(" ?).")]),e("li",null,[n("We say that a code is "),e("a",$,[n("structured"),t(s)]),n(" when we use the well-know control flow structures :if/then/else, loops, functions, lexical scopes for variables. "),ee]),ne,se]),e("p",null,[n("This swift script shows a sample of using "),e("a",te,[n("Task + async/await"),t(s)])]),e("p",null,[n("This swift script shows a sample of using "),e("a",ae,[n("TaskGoup + async/await"),t(s)])]),e("p",null,[n("This swift script shows a sample of "),e("a",oe,[n("TaskGoup cancellation"),t(s)])]),e("p",null,[n("This swift script shows how to "),e("a",ie,[n("convert callbacks into async/await"),t(s)])]),re,e("p",null,[e("a",le,[n("this code"),t(s)]),n(" illustrates some of the above features.")]),ce,e("ul",null,[e("li",null,[e("a",pe,[n("Key-paths"),t(s)]),n(" allow to refer to properties of a type.")]),ue,de]),e("p",null,[e("a",he,[n("this code"),t(s)]),n(" illustrates some of the above features.")]),fe,me,e("p",null,[e("a",be,[n("Please click on this link to view the exercise"),t(s)])]),e("details",ke,[ge,e("p",null,[e("a",we,[n("Solution"),t(s)])])]),_e,e("p",null,[e("a",ve,[n("Please click on this link to view the exercise"),t(s)])]),e("details",ye,[xe,e("p",null,[e("a",Se,[n("Solution"),t(s)])])]),Te,e("p",null,[e("a",qe,[n("Please click on this link to view the exercise"),t(s)])]),e("details",Ie,[Ee,e("p",null,[e("a",Pe,[n("Solution 1"),t(s)])]),e("p",null,[e("a",Ae,[n("Solution 2 with results"),t(s)])])]),Fe,e("ul",null,[e("li",null,[e("a",Ue,[n("Swift official documentation"),t(s)]),n(".")]),e("li",null,[e("a",Ce,[n("Understanding opaque types in Swift"),t(s)])]),e("li",null,[e("a",je,[n("Swift evolution Opaque Result Types"),t(s)])]),e("li",null,[e("a",Ge,[n("Some async examples on SwiftFiddle"),t(s)]),n(" and "),e("a",Oe,[n("how to use Runloop on SwiftFiddle"),t(s)])])])])}const Ne=o(c,[["render",We],["__file","index.html.vue"]]),Be=JSON.parse('{"path":"/swift-part2/","title":"Swift (part 2)","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Object oriented programming features","slug":"object-oriented-programming-features","link":"#object-oriented-programming-features","children":[]},{"level":2,"title":"Structs","slug":"structs","link":"#structs","children":[]},{"level":2,"title":"Opaque types","slug":"opaque-types","link":"#opaque-types","children":[]},{"level":2,"title":"Use structs by default","slug":"use-structs-by-default","link":"#use-structs-by-default","children":[]},{"level":2,"title":"Functional programming features","slug":"functional-programming-features","link":"#functional-programming-features","children":[]},{"level":2,"title":"Structured Concurrency","slug":"structured-concurrency","link":"#structured-concurrency","children":[]},{"level":2,"title":"Generics","slug":"generics","link":"#generics","children":[]},{"level":2,"title":"Key-paths","slug":"key-paths","link":"#key-paths","children":[]},{"level":2,"title":"Exercises","slug":"exercises","link":"#exercises","children":[{"level":3,"title":"Exercise 1","slug":"exercise-1","link":"#exercise-1","children":[]},{"level":3,"title":"Exercise 2","slug":"exercise-2","link":"#exercise-2","children":[]},{"level":3,"title":"Exercise 3","slug":"exercise-3","link":"#exercise-3","children":[]}]},{"level":2,"title":"Sources and more reading","slug":"sources-and-more-reading","link":"#sources-and-more-reading","children":[]}],"git":{"updatedTime":1719480969000,"contributors":[{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":7},{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":6}]},"filePathRelative":"swift-part2/README.md"}');export{Ne as comp,Be as data}; diff --git a/assets/index.html-B8dggsdP.js b/assets/index.html-B8dggsdP.js new file mode 100644 index 0000000..377e5d8 --- /dev/null +++ b/assets/index.html-B8dggsdP.js @@ -0,0 +1 @@ +import{_ as i,r as n,o as l,c as r,a as e,b as t,d as a,e as s}from"./app-Bbun9eEO.js";const c={},d=e("h1",{id:"swift-part-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#swift-part-1"},[e("span",null,"Swift (part 1)")])],-1),u=e("div",{class:"custom-container tip"},[e("p",{class:"custom-container-title"},"Estimated time"),e("p",null,"1/2 day")],-1),h=e("p",null,"Swift is the official programming language for developing iOS, iPadOS, macOS, watchOS and AppleTV apps. It can also target other platforms such as Windows, Linux and Android.",-1),f={href:"https://github.com/swiftlang/swift",target:"_blank",rel:"noopener noreferrer"},p=s('

A quick tour of some features

Swift has modern and interesting features. Here are some notable ones:

  • Swift is statically typed and supports implicit typing.
    • Static typing: types cannot change on runtime (it is the opposite of dynamic typing).
    • Implicit typing: the compiler can infer the type whenever possible.
  • var creates mutable variables.
  • let creates immutable variables or constants.
  • String interpolation is available with this syntax \\(expression).
  • Parenthesis are not required in if, for, while and switch statements.
  • if and switch statements are expressions.
  • for-each is the only type of for loop available.
  • Optionals allows to write code free from null pointer errors (also called Null Safety in other languages).
  • Functional programming is supported (Higher-order functions and functions as 1st class items, etc.).
  • Object oriented programming is supported.
  • Interfaces are called protocols and they are used a lot.
  • Structures are available and provide a lot of features (More on that later).
',3),m={href:"https://swiftfiddle.com/2382a3b3fdc54631140f51bae116dc74",target:"_blank",rel:"noopener noreferrer"},w=e("p",null,"In the following sections, we will delve into more features.",-1),_={class:"custom-container tip"},b=e("p",{class:"custom-container-title"},"++ and -- are removed since swift 3",-1),g={href:"https://github.com/apple/swift-evolution/blob/master/proposals/0004-remove-pre-post-inc-decrement.md",target:"_blank",rel:"noopener noreferrer"},y=s('

Functions

In the this section, the terms argument and parameter are used interchangeably.

The declaration of functions in Swift has the following peculiarities:

  • Parameters are named and ordered. This means that when you call a function, you must specify the name of the arguments in the same order as the declaration.
  • A parameter can have different external and internal names by declaring it like this: externalName internalName: Type. The external name is also called an argument label.
  • You can make a parameter anonymous by setting this external name: _.
  • Arguments can have a default value. These are also called optional arguments.
',4),v={href:"https://swiftfiddle.com/690a3e3bbe580f524f72358ccdb696da",target:"_blank",rel:"noopener noreferrer"},x=s("

Swift allows to use functions as first class items or citizens. This allows to store function references into variables, pass functions as arguments to other functions and return a function from a function. Here is a brief listing of the these features:

  • A function can be assigned to a variable, passed as a function parameter or returned from a function.
  • A function type can be expressed as follows: (typeOfParam1, typeOfParam2, etc) -> returnType.
  • The empty return type is Void.
  • We can use typealias to shorten writing long types.
  • Swift supports anonymous functions (also called lambda function) with the following syntax { argName1, argName2, etc. in // code }
",2),k={href:"https://swiftfiddle.com/5d6b837c869bf23615376bc4cc70bcd1",target:"_blank",rel:"noopener noreferrer"},S=s('

Let's explore in the next section, one of the most amazing features of Swift which is Optionals.

Optionals (aka. Null safety)

In a nutshell, optionals is a compiler feature that allows you to avoid the infamous Null pointer exception or npe. The Swift compiler provides null safety and reports errors and warnings when we manipulate nullable (also called optional) values. Here is a list of null safety features provided by swift:

The name of null in iOS development

In Swift, the null value is called nil

  • All types are non optional by default. This means that we cannot assign nil to a variable or an argument. For example, this code fails var s: String = nil.
  • A type can be made optional by suffixing it with a ?. For example: var s: String? = nil.
  • You cannot call a method or a property of an optional type, unless you do one of those possibilities:
    • Use optional chaining with the ? suffix.
    • Provide a default value with the ?? operator.
    • Unwrap the optional so that it becomes non optional.
    • Force unwrap the optional using the ! suffix. This should never be used as it bypasses compiler checks.

Never unwrap with !

You must never force unwrap with the !. Use other unwrapping techniques instead. On of the rarest exceptions is with Interface builder's Outlets in UIKit @IBOutlet var label: UILabel!. Fortunately, since we are not using UIKit in this training, we will avoid this situation.

',6),T={href:"https://swiftfiddle.com/fa7ad8713475c04666462236db939857",target:"_blank",rel:"noopener noreferrer"},E=s('

Enumerations

Enumerations allow to work with a group of values in a type-safe fashion. Swift provides many interesting features to enumerations:

  • When the compiler can infer it, you can omit the name of the enumeration when you use one of its values.
  • Switch statements support enumerations.
  • You can easily iterate over an enum's values by using : CaseIterable.
  • You can associate values or provide a raw value to enumeration cases. Raw values can be implicitly assigned.
  • You can use another enumeration as associated value, this is called recursive enumeration.
',3),I={href:"https://swiftfiddle.com/d508deb3493e9b572eaf00891c91d8f0",target:"_blank",rel:"noopener noreferrer"},O={href:"https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html",target:"_blank",rel:"noopener noreferrer"},N=e("h2",{id:"error-management",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#error-management"},[e("span",null,"Error management")])],-1),A=e("p",null,[t("Swift provides two ways for error management: "),e("em",null,"Exceptions"),t(" and the "),e("em",null,"Result"),t(" type.")],-1),P=e("code",null,"throw [value]",-1),q=e("code",null,"Error",-1),F={href:"https://www.hackingwithswift.com/example-code/language/how-to-throw-errors-using-strings",target:"_blank",rel:"noopener noreferrer"},W=s("
  • We must call throw when we want to return an error. Throwing in a normal situation is a bad practice.
  • We say that a function throws when it can throw and exception. It must have the throws qualifier.
  • When we call a function that throws, we must precede the call with try keyword
  • When we call a function that throws, we can either propagate its error if it is thrown or handle to stop its propagation.
  • ",4),R=s("
  • The Result type is a an enum that has two possible cases: success(Sucess) or failure(Failure)
    • The failure value must conform to the Error protocol
    • A Result can be handled with usual Swift features for enums: guard, switch, etc.
    • The Result type has can be used with the exception style. Its get() method returns the success value or throws the error.
  • ",1),U={href:"https://swiftfiddle.com/84b40a652f2b31c0b9cd1e0b37b15ca0",target:"_blank",rel:"noopener noreferrer"},Y=e("h2",{id:"some-features-in-bulk",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#some-features-in-bulk"},[e("span",null,"Some features in bulk")])],-1),L={href:"https://swiftfiddle.com/78907b9238bd580e90c9f4b3732e26be",target:"_blank",rel:"noopener noreferrer"},V={href:"https://swiftfiddle.com/507224875d91da9c6257e2e86533b360",target:"_blank",rel:"noopener noreferrer"},B=e("h2",{id:"exercises",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercises"},[e("span",null,"Exercises")])],-1),H=e("h3",{id:"exercise-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-1"},[e("span",null,"Exercise 1")])],-1),z={href:"https://swiftfiddle.com/6a40668c99d1e2cf079be7525548ca60",target:"_blank",rel:"noopener noreferrer"},C={class:"custom-container details"},D=e("summary",null,"Please open to see the solution(s)",-1),G={href:"https://swiftfiddle.com/4e97fc9476694424b0fbab6dd8118c35",target:"_blank",rel:"noopener noreferrer"},K=e("h3",{id:"exercise-2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-2"},[e("span",null,"Exercise 2")])],-1),M={href:"https://swiftfiddle.com/0e980f44cf6855c63f3a9ce772872dde",target:"_blank",rel:"noopener noreferrer"},j={class:"custom-container details"},J=e("summary",null,"Please open to see the solution(s)",-1),Q={href:"https://swiftfiddle.com/1bb9a747f719e0f35ca470c079a1e453",target:"_blank",rel:"noopener noreferrer"},X=e("h3",{id:"exercise-3",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#exercise-3"},[e("span",null,"Exercise 3")])],-1),Z={href:"https://swiftfiddle.com/2a603ce22c3edc9a2bc0cee8bb65885d",target:"_blank",rel:"noopener noreferrer"},$={class:"custom-container details"},ee=e("summary",null,"Please open to see the solution(s)",-1),te={href:"https://swiftfiddle.com/e3c6f484bffcc5945db5dd43ebc11c84",target:"_blank",rel:"noopener noreferrer"},oe=e("h2",{id:"sources",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sources"},[e("span",null,"Sources")])],-1),ae={href:"https://docs.swift.org",target:"_blank",rel:"noopener noreferrer"};function se(ie,ne){const o=n("ExternalLinkIcon");return l(),r("div",null,[d,u,h,e("p",null,[t("The source code of the language toolchain is hosted in "),e("a",f,[t("swiftlang/swift"),a(o)]),t(" GitHub repository.")]),p,e("p",null,[e("a",m,[t("this code"),a(o)]),t(" illustrates some of the features listed above.")]),w,e("div",_,[b,e("p",null,[e("a",g,[t("This post"),a(o)]),t(" details all the problems related to using these operators.")])]),y,e("p",null,[e("a",v,[t("This code"),a(o)]),t(" illustrates the above features.")]),x,e("p",null,[e("a",k,[t("This code"),a(o)]),t(" illustrates these features.")]),S,e("p",null,[e("a",T,[t("This code"),a(o)]),t(" illustrates null safety and how to use optional types.")]),E,e("p",null,[e("a",I,[t("This code"),a(o)]),t(" illustrates some enumeration features. For further reading please consult "),e("a",O,[t("the official documentation"),a(o)]),t(".")]),N,A,e("ul",null,[e("li",null,[t("Exceptions provide an alternate return route with the "),P,t(" keyword. "),e("ul",null,[e("li",null,[t("The thrown value must conform the "),q,t(" protocol. We can even throw a "),e("a",F,[t("String"),a(o)]),t(" that way.")]),W])]),R]),e("p",null,[e("a",U,[t("This code"),a(o)]),t(" illustrates error handling features.")]),Y,e("ul",null,[e("li",null,[t("Swift has 3 collection types out of the box: Array, Dictionary and Set. They are illustrated in "),e("a",L,[t("this code"),a(o)])]),e("li",null,[t("We can use tuples in Swift as shown in "),e("a",V,[t("this example"),a(o)])])]),B,H,e("p",null,[e("a",z,[t("Please click on this link to view the exercise"),a(o)])]),e("details",C,[D,e("p",null,[e("a",G,[t("Solution"),a(o)])])]),K,e("p",null,[e("a",M,[t("Please click on this link to view the exercise"),a(o)])]),e("details",j,[J,e("p",null,[e("a",Q,[t("Solution"),a(o)])])]),X,e("p",null,[e("a",Z,[t("Please click on this link to view the exercise"),a(o)])]),e("details",$,[ee,e("p",null,[e("a",te,[t("Solution"),a(o)])])]),oe,e("ul",null,[e("li",null,[e("a",ae,[t("Swift official documentation"),a(o)]),t(".")])])])}const re=i(c,[["render",se],["__file","index.html.vue"]]),ce=JSON.parse('{"path":"/swift-part1/","title":"Swift (part 1)","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"A quick tour of some features","slug":"a-quick-tour-of-some-features","link":"#a-quick-tour-of-some-features","children":[]},{"level":2,"title":"Functions","slug":"functions","link":"#functions","children":[]},{"level":2,"title":"Optionals (aka. Null safety)","slug":"optionals-aka-null-safety","link":"#optionals-aka-null-safety","children":[]},{"level":2,"title":"Enumerations","slug":"enumerations","link":"#enumerations","children":[]},{"level":2,"title":"Error management","slug":"error-management","link":"#error-management","children":[]},{"level":2,"title":"Some features in bulk","slug":"some-features-in-bulk","link":"#some-features-in-bulk","children":[]},{"level":2,"title":"Exercises","slug":"exercises","link":"#exercises","children":[{"level":3,"title":"Exercise 1","slug":"exercise-1","link":"#exercise-1","children":[]},{"level":3,"title":"Exercise 2","slug":"exercise-2","link":"#exercise-2","children":[]},{"level":3,"title":"Exercise 3","slug":"exercise-3","link":"#exercise-3","children":[]}]},{"level":2,"title":"Sources","slug":"sources","link":"#sources","children":[]}],"git":{"updatedTime":1719253649000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":6},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":5}]},"filePathRelative":"swift-part1/README.md"}');export{re as comp,ce as data}; diff --git a/assets/index.html-C39pB5w2.js b/assets/index.html-C39pB5w2.js new file mode 100644 index 0000000..374e923 --- /dev/null +++ b/assets/index.html-C39pB5w2.js @@ -0,0 +1 @@ +import{_ as a,r as o,o as s,c as r,a as e,b as n,d as i,e as l}from"./app-Bbun9eEO.js";const c={},m=l('

    Welcome

    Prerequisites

    • Basic knowledge of programming
    • Dev environment: macOS and Xcode
    ',4),d={href:"https://developer.apple.com/documentation/",target:"_blank",rel:"noopener noreferrer"},h={href:"https://github.com/worldline/ios-training",target:"_blank",rel:"noopener noreferrer"},p={href:"https://icones8.fr/icon/51974/xcode",target:"_blank",rel:"noopener noreferrer"};function u(f,g){const t=o("ExternalLinkIcon");return s(),r("div",null,[m,e("ul",null,[e("li",null,[e("a",d,[n("Official documentation"),i(t)])]),e("li",null,[e("a",h,[n("GitHub repository for this training"),i(t)])]),e("li",null,[e("a",p,[n("Logo downloaded from icones8"),i(t)])])])])}const b=a(c,[["render",u],["__file","index.html.vue"]]),k=JSON.parse('{"path":"/","title":"Welcome","lang":"en-US","frontmatter":{"home":true,"heroImage":"/logo.png","tagline":"Getting started with iOS development with Swift and SwiftUI","actions":[{"text":"Get started →","link":"/presentation/","type":"primary"}],"features":[{"title":"Swift","details":"Discover the Swift programming language (version 5.10)"},{"title":"SwiftUI","details":"Develop iOS applications"}],"footer":"Worldline, 2021"},"headers":[{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"Useful links","slug":"useful-links","link":"#useful-links","children":[]}],"git":{"updatedTime":1719246851000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":3},{"name":"yassine benabbas (a527524)","email":"yassine.benabbas@worldline.com","commits":2},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":2},{"name":"Sowtch","email":"quentin.cptr@gmail.com","commits":1}]},"filePathRelative":"index.md"}');export{b as comp,k as data}; diff --git a/assets/index.html-CfxHtkWF.js b/assets/index.html-CfxHtkWF.js new file mode 100644 index 0000000..25cf745 --- /dev/null +++ b/assets/index.html-CfxHtkWF.js @@ -0,0 +1,8 @@ +import{_ as a,r,o as s,c as i,a as e,b as t,d as o,e as l}from"./app-Bbun9eEO.js";const c={},d=l(`

    Going further

    Server side development

    • Vapor is a Swift framework that allows to develop servers
    • Install the Vapor cli brew install vapor
    • Create a vapor project vapor new hello-vapor -n
    • Run the server: cd hello-vapor and swift run
    Building for debugging...
    +Build complete! (1.25s)
    +[ NOTICE ] Server starting on http://127.0.0.1:8080
    +
    • Test the server
    curl http://127.0.0.1:8080
    +It works!
    +➜ curl http://127.0.0.1:8080/hello
    +Hello, world!
    +

    Swift and SwoftUI on the browser

    `,7),h={href:"https://swiftwasm.org/",target:"_blank",rel:"noopener noreferrer"},p={href:"https://pad.swiftwasm.org/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/TokamakUI/Tokamak",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/swiftwebui/SwiftWebUI",target:"_blank",rel:"noopener noreferrer"},w=e("h2",{id:"advanced-swift",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#advanced-swift"},[e("span",null,"Advanced Swift")])],-1),m={href:"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Actors",target:"_blank",rel:"noopener noreferrer"},b={href:"https://developer.apple.com/documentation/Swift/AdoptingSwift6",target:"_blank",rel:"noopener noreferrer"},g={href:"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/",target:"_blank",rel:"noopener noreferrer"},v={href:"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/",target:"_blank",rel:"noopener noreferrer"},_=e("h2",{id:"conclusion",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#conclusion"},[e("span",null,"Conclusion")])],-1),k=e("p",null,"This training was introduction on Swift and SwiftUI. It just scratched the surface of developing for the Apple development. There are many things that we didn't explore such as the accessibility, hardware features such as geolocation and the technical aspects of the developer account (certificates, provisioning profiles, etc.).",-1),S={href:"https://developer.apple.com/videos/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://www.raywenderlich.com/",target:"_blank",rel:"noopener noreferrer"},I={href:"https://www.hackingwithswift.com/",target:"_blank",rel:"noopener noreferrer"},x={href:"https://developer.apple.com/swift-playgrounds/",target:"_blank",rel:"noopener noreferrer"},T={href:"https://developer.apple.com/tutorials/app-dev-training",target:"_blank",rel:"noopener noreferrer"};function U(A,W){const n=r("ExternalLinkIcon");return s(),i("div",null,[d,e("ul",null,[e("li",null,[e("a",h,[t("SwiftWasm"),o(n)]),t(" is a project that allows to run Swift code in the browser using WebAssembly.")]),e("li",null,[e("a",p,[t("SwiftWasm Pad"),o(n)]),t(" is an online editor that allows to write SwiftUI code and run it in the browser. It relies on the "),e("a",u,[t("TokamakUI"),o(n)]),t(" framework which is SwiftUI-compatbile framework(or a re-implementation of SwiftUI) for the web.")]),e("li",null,[e("a",f,[t("SwiftWebUI"),o(n)]),t(" is another project that allows to run a server that renders SwiftUI to the browser. Note that it is considered as a toy project by its creator.")])]),w,e("ul",null,[e("li",null,[e("a",m,[t("Swift actors"),o(n)])]),e("li",null,[e("a",b,[t("Swift 6 strict concurrency"),o(n)])]),e("li",null,[e("a",g,[t("Attributes"),o(n)])]),e("li",null,[e("a",v,[t("Macros allow to run actions on code at compile time"),o(n)])])]),_,k,e("p",null,[t("To go further, it is advised to watch the videos from Apple's "),e("a",S,[t("WWDC"),o(n)]),t(' (WorldWide Developer Conference - pronounced "dubdub dee cee"). There many other resources available online that you should pick and choose depending on the needs. Here are some of them:')]),e("ul",null,[e("li",null,[e("a",y,[t("raywenderlich.com"),o(n)])]),e("li",null,[e("a",I,[t("hackingwithswift.com"),o(n)])]),e("li",null,[e("a",x,[t("Swift playgrounds"),o(n)])]),e("li",null,[e("a",T,[t("Official iOS App Dev Tutorials"),o(n)])])])])}const N=a(c,[["render",U],["__file","index.html.vue"]]),B=JSON.parse('{"path":"/to-go-further/","title":"Going further","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Server side development","slug":"server-side-development","link":"#server-side-development","children":[]},{"level":2,"title":"Swift and SwoftUI on the browser","slug":"swift-and-swoftui-on-the-browser","link":"#swift-and-swoftui-on-the-browser","children":[]},{"level":2,"title":"Advanced Swift","slug":"advanced-swift","link":"#advanced-swift","children":[]},{"level":2,"title":"Conclusion","slug":"conclusion","link":"#conclusion","children":[]}],"git":{"updatedTime":1719433280000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":4},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":3},{"name":"yassine benabbas (a527524)","email":"yassine.benabbas@worldline.com","commits":1}]},"filePathRelative":"to-go-further/README.md"}');export{N as comp,B as data}; diff --git a/assets/index.html-ChAAQVq_.js b/assets/index.html-ChAAQVq_.js new file mode 100644 index 0000000..590a17e --- /dev/null +++ b/assets/index.html-ChAAQVq_.js @@ -0,0 +1,3 @@ +import{_ as i,r as n,o as r,c as l,a as e,b as a,d as s,e as o}from"./app-Bbun9eEO.js";const d={},c=e("h1",{id:"locally-persisting-data",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locally-persisting-data"},[e("span",null,"Locally persisting data")])],-1),p=e("div",{class:"custom-container tip"},[e("p",{class:"custom-container-title"},"Estimated time"),e("p",null,"1/4 day")],-1),h=e("p",null,"Persisting data locally consists of keeping app data after the app has been killed and the variables removed from memory. The persisted data offers many advantages. We can use it to show initial data when the app starts and waits for the first batch data to be fetched from the server. It can also be used to allow for offline app usage.",-1),u={class:"custom-container warning"},f=e("p",{class:"custom-container-title"},"iOS isolates app data from other apps",-1),m={href:"https://medium.com/@dinesh.kachhot/different-ways-to-share-data-between-apps-de75a0a46d4a",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"There are many ways to persist data in SwiftUI that we cover below.",-1),g=e("h2",{id:"userdefaults",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#userdefaults"},[e("span",null,"UserDefaults")])],-1),v={href:"https://www.hackingwithswift.com/books/ios-swiftui/storing-user-settings-with-userdefaults",target:"_blank",rel:"noopener noreferrer"},w=o(`

    Here is sample code that shows how to persist and load data.

    UserDefaults.standard.set(self.tapCount, forKey: "Tap")
    +@State private var tapCount = UserDefaults.standard.integer(forKey: "Tap")
    +

    Codable saved in a file

    A more advanced and powerful technique is to manually load and persist a Codable into a file. This technique is useful if you want to store complex objects (such as the state or model) in a JSON file. There are two steps in this process, the first one consists of decoding / encoding the object from / into JSON using JSONDecoder().decode and JSONEncoder().encode. The second step consists of loading / saving the encoded data and we can think of two ways to achieve this. The first one consists of user defaults' dataForKey: to load the data and setObject:ForKey to persist it. Another one consists of creating and managing a file by the developer using file APIs such as fileHandle.availableData to load the data from a file and data.write to save it.

    Sophisticated data persistence libraries

    For storing data in a database or similar fashion, SQLite is available as a low level library. It is not recommended to use it unless there is a strong performance concern. Instead, it is recommended to use libraries specialized in data persistence. Some can be assimilated to an ORM library (Object Relational Mapper). The remainder of this section describes some of them.

    Please be careful about the pricing of cloud storage

    Sophisticated databases generally provide cloud storage to provide a complete offer. If you're interested in storing data in the cloud, please take some time to read the pricing page to avoid any bad surprises when your app runs in production.

    Core Data

    `,8),k={href:"https://developer.apple.com/documentation/coredata",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"It works similarly as an ORM where classes are mapped into tables. Xcode provides a graphical editor that allows to specify the tables, the relations and generate the necessary code (in Swift or Objective-C).",-1),_=e("figure",null,[e("img",{src:"https://docs-assets.developer.apple.com/published/fbb9767e96/rendered2x-1622022015.png",alt:"Core date editor",tabindex:"0",loading:"lazy"}),e("figcaption",null,"Core date editor")],-1),x={href:"https://www.hackingwithswift.com/books/ios-swiftui/how-to-combine-core-data-and-swiftui",target:"_blank",rel:"noopener noreferrer"},S=e("h3",{id:"realm",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#realm"},[e("span",null,"Realm")])],-1),T={href:"https://realm.io/",target:"_blank",rel:"noopener noreferrer"},C=o('

    Firebase datastore (or any other cloud based storage)

    As opposed to Realm and Core Data, which are local first databases, Firebase datastore is a cloud first database. This means that Firebase Datastore requires an internet connection to store and load the data. However, the library is simple to use and supports real time updates.

    TIP

    Firebase datastore is part of a bigger suite of service called Firebase. For example, we can Firebase App Distribution in Firebase, which is a service that allows to deploy and distribute apps without going the burden of using TestFlight.

    PW: complete the official iOS persisting data tutorial

    This PW shows how to save a Codable in a manually managed file using JSON encoder and filesystem APIs.

    ',5),I={href:"https://developer.apple.com/tutorials/app-dev-training/persisting-data",target:"_blank",rel:"noopener noreferrer"};function D(O,F){const t=n("ExternalLinkIcon");return r(),l("div",null,[c,p,h,e("div",u,[f,e("p",null,[a("For security reasons, each app is isolated from the rest of the apps. This is called sandboxing. "),e("a",m,[a("This article"),s(t)]),a(" shows the different ways that allow two or more apps to share their data")])]),b,g,e("p",null,[a("It is a very simple key-value storage that persists data in a file. The API surface is very small and the developer does not need to manage the persisted file. This makes this technique very efficient for simple storage use cases. You can find a short "),e("a",v,[a("guide here"),s(t)]),a(".")]),w,e("p",null,[e("a",k,[a("Core Data"),s(t)]),a(' is the official library to "Persist or cache data on a single device, or sync data to multiple devices with CloudKit". It existed since iOS 3 and Apple continuously updates it to keep it relevant. It also has the reputation of having a steep learning curve, but it remains famous among developers.')]),y,_,e("p",null,[a("Even though Core Date existed before SwiftUI, Apple made sure that both of them can be used together. This article shows "),e("a",x,[a("how to use Core Data in a SwiftUI project"),s(t)]),a(".")]),S,e("p",null,[e("a",T,[a("Realm"),s(t)]),a(" is a high level alternative to SQLite. It can be seen as alternative to Core Data as they seem to provide a similar list of features. Most notably, the possibility to store data locally or in the cloud. The points where Realm wins is that the library seems simpler to learn and to use and that it is also available in Android.")]),C,e("p",null,[e("a",I,[a("https://developer.apple.com/tutorials/app-dev-training/persisting-data"),s(t)])])])}const P=i(d,[["render",D],["__file","index.html.vue"]]),N=JSON.parse('{"path":"/persist-data/","title":"Locally persisting data","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"UserDefaults","slug":"userdefaults","link":"#userdefaults","children":[]},{"level":2,"title":"Codable saved in a file","slug":"codable-saved-in-a-file","link":"#codable-saved-in-a-file","children":[]},{"level":2,"title":"Sophisticated data persistence libraries","slug":"sophisticated-data-persistence-libraries","link":"#sophisticated-data-persistence-libraries","children":[{"level":3,"title":"Core Data","slug":"core-data","link":"#core-data","children":[]},{"level":3,"title":"Realm","slug":"realm","link":"#realm","children":[]},{"level":3,"title":"Firebase datastore (or any other cloud based storage)","slug":"firebase-datastore-or-any-other-cloud-based-storage","link":"#firebase-datastore-or-any-other-cloud-based-storage","children":[]}]},{"level":2,"title":"PW: complete the official iOS persisting data tutorial","slug":"pw-complete-the-official-ios-persisting-data-tutorial","link":"#pw-complete-the-official-ios-persisting-data-tutorial","children":[]}],"git":{"updatedTime":1701178983000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":3}]},"filePathRelative":"persist-data/README.md"}');export{P as comp,N as data}; diff --git a/assets/index.html-Cv2fRjYf.js b/assets/index.html-Cv2fRjYf.js new file mode 100644 index 0000000..48ee3ee --- /dev/null +++ b/assets/index.html-Cv2fRjYf.js @@ -0,0 +1,17 @@ +import{_ as o,r,o as i,c as l,a as e,b as n,d as a,e as t}from"./app-Bbun9eEO.js";const p={},c=t('

    Communicate with a REST API

    Estimated time

    1/4 day

    Some useful concepts

    Communicating with a REST API relies on multiple concepts that we'll cover briefly below:

    ',4),u={href:"https://www.json.org/json-en.html",target:"_blank",rel:"noopener noreferrer"},d={href:"https://www.redhat.com/en/topics/api/what-is-a-rest-api",target:"_blank",rel:"noopener noreferrer"},h={href:"https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages",target:"_blank",rel:"noopener noreferrer"},m=e("em",null,"body",-1),f={href:"https://developer.apple.com/documentation/swift/codable",target:"_blank",rel:"noopener noreferrer"},k=e("code",null,"Serializable",-1),b=e("li",null,[e("code",null,"async"),n(" and "),e("code",null,"await"),n(": these keywords are used to call an asynchronous function using a synchronous coding fashion. This means that callbacks are needed no more!")],-1),v={href:"https://developer.apple.com/documentation/foundation/urlsession",target:"_blank",rel:"noopener noreferrer"},_={href:"https://github.com/apple/swift-corelibs-foundation",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"pw-call-a-rest-api",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#pw-call-a-rest-api"},[e("span",null,"PW: call a REST API")])],-1),w={href:"https://www.hackingwithswift.com/books/ios-swiftui/sending-and-receiving-codable-data-with-urlsession-and-swiftui",target:"_blank",rel:"noopener noreferrer"},q={href:"https://itunes.apple.com/search?term=taylor+swift&entity=song",target:"_blank",rel:"noopener noreferrer"},T=t(`
    {
    +   "resultCount":50,
    +   "results":[
    +      {
    +         "wrapperType":"track",
    +         "kind":"song",
    +         "artistId":159260351,
    +         "collectionId":1440913923,
    +         "trackId":1440914010,
    +         "artistName":"Taylor Swift",
    +         "collectionName":"Taylor Swift (Bonus Track Version)",
    +         "trackName":"Our Song",
    +         "collectionCensoredName":"Taylor Swift (Bonus Track Version)",
    +      }
    +   ]
    +}
    +
    `,1);function y(S,P){const s=r("ExternalLinkIcon");return i(),l("div",null,[c,e("ul",null,[e("li",null,[e("a",u,[n("JSON"),a(s)]),n(": a standard data interchange format used a lot in the HTTP messages of REST APIs.")]),e("li",null,[e("a",d,[n("REST API"),a(s)]),n(": it is a standard communication interface between a client and conforms to the constraints of REST architectural style. In a REST API, HTTP messages are stateless and use JSON data format.")]),e("li",null,[e("a",h,[n("HTTP messages"),a(s)]),n(": an http message is a textual message that contains different parts and can be either a request or a response. A request is the HTTP message that the client sends to the server and response is the HTTP message that the server sends to the client in reaction to the request. Both requests and responses have a part called a "),m,n(". In rest APIs, the body is generally formatted in JSON.")]),e("li",null,[e("a",f,[n("Codable"),a(s)]),n(": it is a type that can convert itself into and out of an external representation. It is equivalent to "),k,n(" in Java. This type is helpful if we want to convert an object into an out of a JSON string.")]),b,e("li",null,[e("a",v,[n("URLSession"),a(s)]),n(": The official iOS HTTP client which is part of the Foundation library. This library is not part of the Swift standard library but there is an implementation for non-Apple platforms which is called "),e("a",_,[n("swift-corelibs-foundation"),a(s)]),n(".")])]),g,e("p",null,[n("This PW relies on the "),e("a",w,[n("excellent tutorial from hackingwithswift"),a(s)]),n(". It guides you on how to fetch JSON data from "),e("a",q,[n("iTunes's API"),a(s)]),n(". Please find and excerpt of the response body below.")]),T])}const x=o(p,[["render",y],["__file","index.html.vue"]]),E=JSON.parse('{"path":"/api-communication/","title":"Communicate with a REST API","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Some useful concepts","slug":"some-useful-concepts","link":"#some-useful-concepts","children":[]},{"level":2,"title":"PW: call a REST API","slug":"pw-call-a-rest-api","link":"#pw-call-a-rest-api","children":[]}],"git":{"updatedTime":1660750594000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":2}]},"filePathRelative":"api-communication/README.md"}');export{x as comp,E as data}; diff --git a/assets/index.html-DKuyFSlS.js b/assets/index.html-DKuyFSlS.js new file mode 100644 index 0000000..b1f6828 --- /dev/null +++ b/assets/index.html-DKuyFSlS.js @@ -0,0 +1,28 @@ +import{_ as o,r as i,o as l,c,a as n,b as a,d as e,e as t}from"./app-Bbun9eEO.js";const p="/ios-training/assets/swftui-playground-MsNlfoqb.png",r="/ios-training/assets/hello-swiftui-DCdT_J-Q.png",u={},d=t('

    UI development

    Estimated time

    1/2 day

    Apple provides two official UI frameworks : UIKit and SwiftUI.

    UIKit is the originally used framework for UI development. It relies on defining the UI in a separate file (storyboard or xib) and the behavior in a swift file. In 2019, Apple release the first version of SwiftUI.

    The remainder of this training focuses on SwiftUI.

    SwiftUI

    SwiftUI brings a new approach to build UIs that we can summarize as follows:

    • All the UI is defined in Swift code. Neither Storyboards nor xibs are needed anymore.
    • The UI is defined in a declarative style.
    • States and bindings allow to hold the app data. The app UI updates automatically when these data change.
    • UI elements are structs that conform to the View protocol.
    • Complex views can be defined by breaking them into smaller views. This is called view composition.
    • The modifier technique is used to apply modifications to a view. A modifier returns a new view each time.
    ',8),m={href:"https://developer.apple.com/documentation/swiftui",target:"_blank",rel:"noopener noreferrer"},f=n("h2",{id:"prerequisites",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#prerequisites"},[n("span",null,"Prerequisites")])],-1),h={href:"https://www.apple.com/swift/playgrounds/",target:"_blank",rel:"noopener noreferrer"},k={href:"https://swiftui-playground.kishikawakatsumi.com/",target:"_blank",rel:"noopener noreferrer"},v=n("figure",null,[n("img",{src:p,alt:"SwiftUI web playground",tabindex:"0",loading:"lazy"}),n("figcaption",null,"SwiftUI web playground")],-1),w={href:"https://github.com/compnerd/swift-win32",target:"_blank",rel:"noopener noreferrer"},b=t(`

    Anatomy of a basic view

    The following code shows a sample view.

    struct ContentView: View {
    +    var body: some View {
    +        VStack {
    +            Text("Hello SwiftUI")
    +                .font(.largeTitle)
    +                .foregroundColor(.blue)
    +                .padding()
    +            Button(action: {}) {
    +                HStack {
    +                    Image(systemName: "suit.heart.fill")
    +                        .foregroundColor(.red)
    +                    Text("I am a button")
    +                        .font(.headline)
    +                        .foregroundColor(.white)
    +                }
    +                .padding(12)
    +                .background(Color.orange)
    +                .cornerRadius(8)
    +            }
    +        }
    +    }
    +}
    +

    As noted earlier, SwiftUI views are structs that conforms to the View protocol. This protocol defined a computed property that returns a View as an opaque type.

    The body of the view has a VStack as its root element. A VStack is a container view that arrange its direct children vertically (on a column). The first child is a Text view and its second child is a Button.

    The Text view chains calls to some methods that we call modifiers. They allow to do anything that we want to the view that called it and they return a new View instance. This means that we can apply another modifier to the result of a modifier and so on (this is called chaining). This allows modifiers to have a declarative syntax that makes the code easy to understand. SwiftUI provides built-in modifiers and allows us to create custom ones. Can you match all the modifiers used in the code and their effects ?

    The modifiers used are:
    font(...)
    +foregroundColor(...)
    +padding(...)
    +background(...)
    +cornerRadius(...)
    +

    The button has no action, meaning that is does nothing on click and its content is defined as an HStack. An HStack is a container view that arrange its direct children horizontally (on a row). The stack contains an image and a button.

    The view renders as illustrated by the image below.

    Hello SwiftUI
    Hello SwiftUI

    Let's do more SwiftUI.

    A summary of important concepts

    • @State: Single source of truth of a view and should not be shared with other views.
    • @Binding: allows to pass a reference of a state to a child view using $state.
    • @EnvironmentObject: Allows to globally share data between views. An @EnvironmentObject conforms to the ObservableObject protocol and its properties have the @Published property wrapper.
    • @ObservedObject: Allows to observe changes in an object that conforms to the ObservableObject protocol.

    PW: complete some official SwiftUI tutorials

    ',14),g={href:"https://developer.apple.com/tutorials/swiftui",target:"_blank",rel:"noopener noreferrer"},_=n("p",null,"Please cover these tutorials to get a good grasp of SwiftUI.",-1),y={href:"https://developer.apple.com/tutorials/swiftui/creating-and-combining-views",target:"_blank",rel:"noopener noreferrer"},I={href:"https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation",target:"_blank",rel:"noopener noreferrer"},S={href:"https://developer.apple.com/tutorials/swiftui/handling-user-input",target:"_blank",rel:"noopener noreferrer"},U={href:"https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions",target:"_blank",rel:"noopener noreferrer"},x={href:"https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces",target:"_blank",rel:"noopener noreferrer"},T={href:"https://developer.apple.com/tutorials/swiftui/working-with-ui-controls",target:"_blank",rel:"noopener noreferrer"};function A(q,V){const s=i("ExternalLinkIcon");return l(),c("div",null,[d,n("p",null,[a("The "),n("a",m,[a("official documentation of SwiftUI is available here"),e(s)]),a(".")]),f,n("p",null,[a("It is recommended to use Xcode to learn and create SwiftUI apps. For simple apps, we can use the "),n("a",h,[a("Swift Playgrounds"),e(s)]),a(" app. There is a "),n("a",k,[a("web playground"),e(s)]),a(" that can be exceptionally used. You can see a screenshot of the tool below.")]),v,n("p",null,[a("Another promising alternative to watch is "),n("a",w,[a("compnerd's windows port"),e(s)]),a(" of UIKit and SwiftUI. So, if you can have a recent version Xcode running, this is be the best IDE for SwiftUI development.")]),b,n("p",null,[a("Apple provides a comprehensive "),n("a",g,[a("SwiftUI tutorial"),e(s)]),a(" that covers most of the basic use cases such as creating views and handling inputs, animations and transitions.")]),_,n("ul",null,[n("li",null,[a("Basic layout "),n("ul",null,[n("li",null,[n("a",y,[a("Creating and combining views"),e(s)]),a(" (40 min)")]),n("li",null,[n("a",I,[a("Building lists and navigation"),e(s)]),a(" (35 min)")]),n("li",null,[n("a",S,[a("Handling user input"),e(s)]),a(" (20 min)")])])]),n("li",null,[a("Animations and complex layouts "),n("ul",null,[n("li",null,[n("a",U,[a("Animating views and transitions"),e(s)]),a(" (20 min)")]),n("li",null,[n("a",x,[a("Composing complex interfaces"),e(s)]),a(" (20 min)")]),n("li",null,[n("a",T,[a("Working with UI controls"),e(s)]),a(" (25 min)")])])])])])}const B=o(u,[["render",A],["__file","index.html.vue"]]),E=JSON.parse('{"path":"/ui-development/","title":"UI development","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"SwiftUI","slug":"swiftui","link":"#swiftui","children":[]},{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"Anatomy of a basic view","slug":"anatomy-of-a-basic-view","link":"#anatomy-of-a-basic-view","children":[]},{"level":2,"title":"A summary of important concepts","slug":"a-summary-of-important-concepts","link":"#a-summary-of-important-concepts","children":[]},{"level":2,"title":"PW: complete some official SwiftUI tutorials","slug":"pw-complete-some-official-swiftui-tutorials","link":"#pw-complete-some-official-swiftui-tutorials","children":[]}],"git":{"updatedTime":1719475184000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"ui-development/README.md"}');export{B as comp,E as data}; diff --git a/assets/index.html-DOlN3MeL.js b/assets/index.html-DOlN3MeL.js new file mode 100644 index 0000000..28abb77 --- /dev/null +++ b/assets/index.html-DOlN3MeL.js @@ -0,0 +1 @@ +import{_ as i,r,o as s,c as l,a as e,b as t,d as a,e as o}from"./app-Bbun9eEO.js";const c="/ios-training/assets/swiftui-framework-wwdc-OMsLBsB_.jpg",p="/ios-training/assets/xcodes-DaBHvxPG.png",d="/ios-training/assets/xcode-new-project-BgcBHlMK.png",h={},f=o('

    Presentation

    Welcompe the world of iOS development

    iOS development consists of developing applications that can target mainly the iPhone and iPad but also macOS, iWatch and Apple TV. There are many ways to achieve this:

    • Using the official frameworks and tools provided by Apple
    • Using 3rd party frameworks and tools such as Capacitor, MAUI and Flutter

    This training focuses on iOS development using the official tools and frameworks proposed by Apple. Our development stack will consist of the following items:

    • Programming language: Swift
    • UI Framework: SwiftUI
    • IDEs: Xcode and Swift Playgrounds

    In addition to that, It is also possible to leverage the Swift language (without SwiftUI) in order to develop console apps and servers on Window, Linux, macOS.

    History

    The early days of iOS development used the Objective-C language, the UIKit UI Framework and -the good old- Xcode. This ecosystem was basic but quite powerful and allowed to develop amazing apps. The continuous updates from Apple improved the developer experience. For example, memory management became automatic (thanks to ARC) and the layout system became capable of adapting to different screen sizes.

    In WWDC 2014, Apple announced the Swift language as an Open Source modern replacement to Objective-C. Following that, apple announced during the next WWDC SwiftUI as the replacement for UIKit.

    swiftui
    swiftui

    As of 2021, the majority of new iOS projects use Swift and SwiftUI with UIKit as a fallback for the UI aspects.

    Getting started

    ',13),u=e("a",{href:""},"Xcodes",-1),g={href:"https://apps.apple.com/us/app/swift-playgrounds/id1496833156?mt=12",target:"_blank",rel:"noopener noreferrer"},m=e("ul",null,[e("li",null,"Xcodes is a tools that downloads and manages the different versions of Xcode.")],-1),w=e("figure",null,[e("img",{src:p,alt:"xcodes",tabindex:"0",loading:"lazy"}),e("figcaption",null,"xcodes")],-1),_={href:"https://swiftforwindows.github.io/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://swift.org/download/#releases",target:"_blank",rel:"noopener noreferrer"},b=e("li",null,"You can use VSCode or Fleet as an IDE.",-1),k={href:"https://github.com/compnerd/swift-win32",target:"_blank",rel:"noopener noreferrer"},S=e("li",null,[t("Open a terminal and run the following command to check if Swift is installed "),e("code",null,"swift --version"),t(".")],-1),v=o('

    Create a CLI app with swift CLI

    • Open a terminal in an empty folder mkdir MyCLI and then cd MyCLI
    • Create the project with swift package init --name MyCLI --type executable
    • and run it with swift run. It should print -> Hello world

    Create an App using Xcode or Swift playgrounds

    • On Xcode, create a either a Project or Playground and run it.
    xcode-new-project.png
    xcode-new-project.png

    Swift project managers

    ',6),x={href:"https://swift.org/package-manager/",target:"_blank",rel:"noopener noreferrer"},I=e("li",null,"Xcode projects are projects fully managed by Xcode (.xcodeproj)",-1),O={href:"https://github.com/Carthage/Carthage",target:"_blank",rel:"noopener noreferrer"},C={href:"https://cocoapods.org/",target:"_blank",rel:"noopener noreferrer"},j=e("p",null,"In this training, we'll use swiftpm and Xcode.",-1),U=e("h2",{id:"links-and-references",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#links-and-references"},[e("span",null,"Links and references")])],-1),A={href:"https://9to5mac.com/2019/06/03/apple-announces-swiftui-a-modern-declarative-user-interface-framework-for-apple-platforms/",target:"_blank",rel:"noopener noreferrer"},L={href:"https://www.apple.com/newsroom/2015/12/03Apple-Releases-Swift-as-Open-Source/",target:"_blank",rel:"noopener noreferrer"},P={href:"https://www.sutori.com/en/story/the-history-of-ios-app-development--hzFfwkD2KYLaa5WrxsrUFGMh",target:"_blank",rel:"noopener noreferrer"},W={href:"https://www.timetoast.com/timelines/history-of-ios",target:"_blank",rel:"noopener noreferrer"};function X(T,B){const n=r("ExternalLinkIcon");return s(),l("div",null,[f,e("ul",null,[e("li",null,[t("On macOS, install "),u,t(" and "),e("a",g,[t("Swift Playgrounds"),a(n)]),t(". "),m])]),w,e("ul",null,[e("li",null,[t("For windows and Linux users you can install "),e("a",_,[t("Swift for Windows"),a(n)]),t(" or "),e("a",y,[t("Swift for Ubuntu"),a(n)]),t(". This will allow you to run Swift code on your machine. "),e("ul",null,[b,e("li",null,[t("Unfortunately, SwiftUI development either challenging or not supported. (on Windows you can try to use "),e("a",k,[t("swift-win32"),a(n)]),t(")")])])]),S]),v,e("ul",null,[e("li",null,[t("Official tools: "),e("ul",null,[e("li",null,[e("a",x,[t("Swift Package Manager"),a(n)]),t(" (also called swiftpm) is the official tool to manage Swift projects. It is platform and IDE agnostic.")]),I])]),e("li",null,[t("3rd party tools: "),e("ul",null,[e("li",null,[e("a",O,[t("Carthage"),a(n)])]),e("li",null,[e("a",C,[t("CocoaPods"),a(n)])])])])]),j,U,e("ul",null,[e("li",null,[e("a",A,[t("Apple announces SwiftUI, a modern declarative user interface framework for Apple platforms"),a(n)])]),e("li",null,[e("a",L,[t("Apple Releases Swift as Open Source"),a(n)])]),e("li",null,[e("a",P,[t("The History of iOS App Development (from iOS 1 to 8)"),a(n)])]),e("li",null,[e("a",W,[t("History of iOS from 2007 to 2018"),a(n)])])])])}const F=i(h,[["render",X],["__file","index.html.vue"]]),M=JSON.parse('{"path":"/presentation/","title":"Presentation","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Welcompe the world of iOS development","slug":"welcompe-the-world-of-ios-development","link":"#welcompe-the-world-of-ios-development","children":[]},{"level":2,"title":"History","slug":"history","link":"#history","children":[]},{"level":2,"title":"Getting started","slug":"getting-started","link":"#getting-started","children":[{"level":3,"title":"Create a CLI app with swift CLI","slug":"create-a-cli-app-with-swift-cli","link":"#create-a-cli-app-with-swift-cli","children":[]},{"level":3,"title":"Create an App using Xcode or Swift playgrounds","slug":"create-an-app-using-xcode-or-swift-playgrounds","link":"#create-an-app-using-xcode-or-swift-playgrounds","children":[]}]},{"level":2,"title":"Swift project managers","slug":"swift-project-managers","link":"#swift-project-managers","children":[]},{"level":2,"title":"Links and references","slug":"links-and-references","link":"#links-and-references","children":[]}],"git":{"updatedTime":1719246851000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"yassine benabbas (a527524)","email":"yassine.benabbas@worldline.com","commits":2},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"presentation/README.md"}');export{F as comp,M as data}; diff --git a/assets/index.html-z4YYCP43.js b/assets/index.html-z4YYCP43.js new file mode 100644 index 0000000..b739a10 --- /dev/null +++ b/assets/index.html-z4YYCP43.js @@ -0,0 +1,84 @@ +import{_ as i,r as o,o as p,c as l,a as n,b as s,d as t,e}from"./app-Bbun9eEO.js";const c={},u=n("h1",{id:"mini-project",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#mini-project"},[n("span",null,"Mini project")])],-1),r=n("p",null,"The final chapter of this training will ask you to create a SwiftUI app from scratch.",-1),d=n("h2",{id:"requirements",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#requirements"},[n("span",null,"Requirements")])],-1),k=n("p",null,"The app consists of a movie explorer app with the following features:",-1),v=n("li",null,"Search for movies by title.",-1),m=n("li",null,"View the details of the selected movie.",-1),h=n("li",null,"The app requires the user to be logged in.",-1),b=n("li",null,"The app allows a new user to register.",-1),g=n("li",null,"The movie list screen allows to logout from the app.",-1),w=n("li",null,"The app remembers the logged in user after a restart.",-1),f={href:"https://movie-api-ybwl.koyeb.app/api-docs/",target:"_blank",rel:"noopener noreferrer"},_=n("ul",null,[n("li",null,[s("The "),n("strong",null,"/movies/search"),s(" endpoint requires to pass the token retrieved from endpoint "),n("strong",null,"/user/login"),s(" or "),n("strong",null,"user/register"),s(" in this header: "),n("code",null,"Authorization: Bearer \\(userResponse.token)")])],-1),y=n("li",null,"(Optional) The result of previous queries is locally cached.",-1),q=n("li",null,"(Optional) Add movie to local favorites ⭐️",-1),T={href:"https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-and-remove-views-with-a-transition",target:"_blank",rel:"noopener noreferrer"},S=n("p",null,"A preview of the app can be seen here.",-1),x=n("iframe",{width:"720",height:"576",src:"https://www.youtube.com/embed/vh5AlaGK0Eo",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:""},null,-1),I=e(`

    Hints

    • There are many techniques to handle the flow from the login view to the movie list view. On of them is to rely on a logged state. The following gives an overview how it looks like.
    struct ContentView: View {
    +    @State var loggedIn: false
    +    
    +    var body: some View {
    +        if loggedIn {
    +            MovieListView()
    +        } else {
    +            // The LoginView takes a callback that is called when the login succeeds
    +            LoginView { newLoggedIn in
    +                loggedIn = newLoggedIn
    +            }
    +        }
    +    }
    +}
    +
    • In the login view, use an enum to track the state of the login operation so that you can disable the login button when a request is running.
    enum LoginState {
    +    case neutral, loading, success, failure
    +}
    +struct LoginView: View {
    +    @State private var loginState: LoginState = .neutral
    +    // other code
    +}
    +
    • Use a Task object to run async code.
    Button("Login") { 
    +    loginState = .loading
    +    Task {
    +        if await login() {
    +            onLoginSuccess(true)
    +        }
    +    }
    +}
    +

    Swift Concurrency crashes on Swift Playground

    Do not use the Swift Playground app to run you app as it does not work well with SwiftUI + Swift Concurrency (async, await and Task). Instead, you can create an Xcode project of type Playground to combine the power of Xcode and the simplicity of Playground projects.

    `,8),L={href:"https://github.com/Tunous/DebouncedOnChange",target:"_blank",rel:"noopener noreferrer"},V=n("li",null,[s("To generate the initial code for a preview, open a view and then use the Xcode feature "),n("em",null,"Editor -> Create preview")],-1),A=n("li",null,[s("The List view requires that you specify an "),n("code",null,"id"),s(" field "),n("code",null,"List(movies, id: \\.title)"),s(" or that the items conform to Identifiable protocol")],-1),C=n("li",null,[s("If you can't add SwiftPM packages from Xcode, add them by editing the "),n("em",null,"package.swift"),s(" file by hand. Here is an example below.")],-1),O=e(`
    // swift-tools-version: 5.6
    +
    +// WARNING:
    +// This file is automatically generated.
    +// Do not edit it by hand because the contents will be replaced.
    +
    +import PackageDescription
    +import AppleProductTypes
    +
    +let package = Package(
    +    name: "Moovy",
    +    platforms: [
    +        .iOS("15.2"),
    +        .macOS("13.0")
    +    ],
    +    products: [
    +        .iOSApplication(
    +            name: "Moovy",
    +            targets: ["AppModule"],
    +            displayVersion: "1.0",
    +            bundleVersion: "1",
    +            appIcon: .placeholder(icon: .sun),
    +            accentColor: .presetColor(.indigo),
    +            supportedDeviceFamilies: [
    +                .pad,
    +                .phone
    +            ],
    +            supportedInterfaceOrientations: [
    +                .portrait,
    +                .landscapeRight,
    +                .landscapeLeft,
    +                .portraitUpsideDown(.when(deviceFamilies: [.pad]))
    +            ],
    +            capabilities: [
    +                .outgoingNetworkConnections()
    +            ],
    +            appCategory: .entertainment
    +        )
    +    ],
    +    dependencies: [
    +        .package(url: "https://github.com/Tunous/DebouncedOnChange.git", "1.0.0"..<"2.0.0"),
    +        .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", "4.0.0"..<"5.0.0")
    +    ],
    +    targets: [
    +        .executableTarget(
    +            name: "AppModule",
    +            dependencies: [
    +                "DebouncedOnChange",
    +                "KeychainAccess"
    +            ],
    +            path: "."
    +        )
    +    ]
    +)
    +
    `,1);function D(P,j){const a=o("ExternalLinkIcon");return p(),l("div",null,[u,r,d,k,n("ul",null,[v,m,h,b,g,w,n("li",null,[s("The app uses "),n("a",f,[s("this API"),t(a)]),s(" for the authenticating and searching for movies. "),_]),y,q,n("li",null,[s("(Optional) Animate the transition between the login view and the movie list view ("),n("a",T,[s("tutorial"),t(a)]),s(").")])]),S,x,I,n("ul",null,[n("li",null,[s("Use "),n("a",L,[s("DebouncedOnChange"),t(a)]),s(" Swift package to optimize search.")]),V,A,C]),O])}const N=i(c,[["render",D],["__file","index.html.vue"]]),E=JSON.parse('{"path":"/mini-project/","title":"Mini project","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Requirements","slug":"requirements","link":"#requirements","children":[]},{"level":2,"title":"Hints","slug":"hints","link":"#hints","children":[]}],"git":{"updatedTime":1701187541000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":4},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"mini-project/README.md"}');export{N as comp,E as data}; diff --git a/assets/style-DGf4msjw.css b/assets/style-DGf4msjw.css new file mode 100644 index 0000000..635f241 --- /dev/null +++ b/assets/style-DGf4msjw.css @@ -0,0 +1 @@ +.vp-back-to-top-button{position:fixed!important;bottom:4rem;inset-inline-end:1rem;z-index:100;width:48px;height:48px;padding:8px;border-width:0;border-radius:50%;background:var(--back-to-top-bg-color);color:var(--back-to-top-color);box-shadow:2px 2px 10px 4px var(--back-to-top-shadow);cursor:pointer}@media (max-width: 959px){.vp-back-to-top-button{transform:scale(.8);transform-origin:100% 100%}}@media print{.vp-back-to-top-button{display:none}}.vp-back-to-top-button:hover{color:var(--back-to-top-color-hover)}.vp-back-to-top-button .back-to-top-icon{overflow:hidden;width:100%;height:100%;background:currentcolor;border-radius:50%;-webkit-mask-image:var(--back-to-top-icon);mask-image:var(--back-to-top-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:cover;mask-size:cover}.vp-scroll-progress{position:absolute;right:-2px;bottom:-2px;width:52px;height:52px}.vp-scroll-progress svg{width:100%;height:100%}.vp-scroll-progress circle{opacity:.9;transform:rotate(-90deg);transform-origin:50% 50%}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--back-to-top-z-index: 5;--back-to-top-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M512%20843.2c-36.2%200-66.4-13.6-85.8-21.8-10.8-4.6-22.6%203.6-21.8%2015.2l7%20102c.4%206.2%207.6%209.4%2012.6%205.6l29-22c3.6-2.8%209-1.8%2011.4%202l41%2064.2c3%204.8%2010.2%204.8%2013.2%200l41-64.2c2.4-3.8%207.8-4.8%2011.4-2l29%2022c5%203.8%2012.2.6%2012.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6%208.2-49.6%2021.8-85.8%2021.8'/%3e%3cpath%20d='m795.4%20586.2-96-98.2C699.4%20172%20513%2032%20513%2032S324.8%20172%20324.8%20488l-96%2098.2c-3.6%203.6-5.2%209-4.4%2014.2L261.2%20824c1.8%2011.4%2014.2%2017%2023.6%2010.8L419%20744s41.4%2040%2094.2%2040%2092.2-40%2092.2-40l134.2%2090.8c9.2%206.2%2021.6.6%2023.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14M513%20384c-34%200-61.4-28.6-61.4-64s27.6-64%2061.4-64c34%200%2061.4%2028.6%2061.4%2064S547%20384%20513%20384'/%3e%3c/svg%3e");--back-to-top-bg-color: #fff;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3;--back-to-top-shadow: rgb(0 0 0 / 20%)}div[class*=language-]:hover:before{display:none}div[class*=language-]:hover .vp-copy-code-button{opacity:1}.vp-copy-code-button{position:absolute;top:.5em;right:.5em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-width:0;border-radius:.5rem;background:transparent;outline:none;opacity:0;cursor:pointer;transition:opacity .4s}@media print{.vp-copy-code-button{display:none}}.vp-copy-code-button:focus,.vp-copy-code-button.copied{opacity:1}.vp-copy-code-button:hover,.vp-copy-code-button.copied{background:var(--copy-code-hover)}.vp-copy-code-button.copied .vp-copy-icon{-webkit-mask-image:var(--code-copied-icon);mask-image:var(--code-copied-icon)}.vp-copy-code-button.copied:after{content:attr(data-copied);position:absolute;top:0;right:calc(100% + .25rem);display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--copy-code-hover);color:var(--copy-code-color);font-weight:500;line-height:1.25rem;white-space:nowrap}.vp-copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;background:currentcolor;color:var(--copy-code-color);font-size:1.25rem;-webkit-mask-image:var(--code-copy-icon);mask-image:var(--code-copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}:root{--code-copy-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202'%20/%3e%3c/svg%3e");--code-copied-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202m-6%209%202%202%204-4'%20/%3e%3c/svg%3e");--copy-code-color: #9e9e9e;--copy-code-hover: rgb(0 0 0 / 50%)}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}.vp-page-meta{max-width:var(--content-width);margin:0 auto;padding:.75rem 2.5rem;display:flex;flex-wrap:wrap;justify-content:space-between;overflow:auto}@media (max-width: 959px){.vp-page-meta{padding:2rem}}@media (max-width: 419px){.vp-page-meta{padding:1.5rem}}@media print{.vp-page-meta{margin:0!important;padding-inline:0!important}}@media (max-width: 719px){.vp-page-meta{display:block}}.vp-page-meta .vp-meta-item{flex-grow:1}.vp-page-meta .vp-meta-item .vp-meta-label{font-weight:500}.vp-page-meta .vp-meta-item .vp-meta-label:not(a){color:var(--c-text-lighter)}.vp-page-meta .vp-meta-item .vp-meta-info{color:var(--c-text-quote);font-weight:400}.vp-page-meta .git-info{text-align:end}.vp-page-meta .edit-link{margin-top:.25rem;margin-bottom:.25rem;margin-inline-end:.5rem;font-size:14px}@media print{.vp-page-meta .edit-link{display:none}}.vp-page-meta .edit-link .icon{position:relative;bottom:-.125em;width:1em;height:1em;margin-inline-end:.25em}.vp-page-meta .last-updated,.vp-page-meta .contributors{margin-top:.25rem;margin-bottom:.25rem;font-size:14px}@media (max-width: 719px){.vp-page-meta .last-updated,.vp-page-meta .contributors{font-size:13px;text-align:start}}.vp-page-nav{display:flex;flex-wrap:wrap;max-width:var(--content-width, 740px);min-height:2rem;margin-inline:auto;margin-top:0;padding-block:.5rem;padding-inline:2rem;border-top:1px solid var(--c-border);transition:border-top var(--t-color);padding-top:1rem;padding-bottom:0}@media (max-width: 959px){.vp-page-nav{padding-inline:1rem}}@media print{.vp-page-nav{display:none}}.vp-page-nav .route-link{display:inline-block;flex-grow:1;margin:.25rem;padding:.25rem .5rem;border:1px solid var(--c-border);border-radius:.25rem}.vp-page-nav .route-link:hover{background:var(--c-bg-light)}.vp-page-nav .route-link .hint{color:var(--c-text-quote);font-size:.875rem;line-height:2}.vp-page-nav .prev{text-align:start}.vp-page-nav .next{text-align:end}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--c-code-group-tab-title: rgba(255, 255, 255, .9);--c-code-group-tab-bg: var(--code-bg-color);--c-code-group-tab-outline: var(var(--c-code-group-tab-title));--c-code-group-tab-active-border: var(--c-brand);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.vp-back-to-top-button{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light);--back-to-top-bg-color: var(--c-bg)}.vp-catalog-wrapper{--catalog-bg-color: var(--c-bg);--catalog-bg-secondary-color: var(--c-bg-dark);--catalog-border-color: var(--c-border);--catalog-active-color: var(--c-brand);--catalog-hover-color: var(--c-brand-light)}.waline-wrapper{--waline-bg-color: var(--c-bg);--waline-bg-color-light: var(--c-bg-light);--waline-text-color: var(--c-color);--waline-border: 1px solid var(--c-border);--waline-border-color: var(--c-border);--waline-theme-color: var(--c-brand);--waline-active-color: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}body{--photo-swipe-bullet: var(--c-bg);--photo-swipe-bullet-active: var(--c-brand)}body{--pwa-text-color: var(--c-text);--pwa-bg-color: var(--c-bg);--pwa-border-color: var(--c-brand);--pwa-btn-text-color: var(--c-bg);--pwa-btn-bg-color: var(--c-brand);--pwa-btn-hover-bg-color: var(--c-brand-light)}.language-modal-mask{--redirect-bg-color: var(--c-bg);--redirect-bg-color-light: var(--c-bg-light);--redirect-bg-color-lighter: var(--c-bg-lighter);--redirect-text-color: var(--c-text);--redirect-primary-color: var(--c-brand);--redirect-primary-hover-color: var(--c-brand-light);--redirect-primary-text-color: var(--c-bg)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html.dark body{--pwa-shadow-color: rgb(0 0 0 / 30%);--pwa-content-color: #ccc;--pwa-content-light-color: #999}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1 .header-anchor,h2 .header-anchor,h3 .header-anchor,h4 .header-anchor,h5 .header-anchor,h6 .header-anchor{color:inherit;text-decoration:none;position:relative}h1 .header-anchor:hover:before,h2 .header-anchor:hover:before,h3 .header-anchor:hover:before,h4 .header-anchor:hover:before,h5 .header-anchor:hover:before,h6 .header-anchor:hover:before{font-size:.8em;content:"¶";position:absolute;left:-.75em;color:var(--c-brand)}h1 .header-anchor:focus-visible,h2 .header-anchor:focus-visible,h3 .header-anchor:focus-visible,h4 .header-anchor:focus-visible,h5 .header-anchor:focus-visible,h6 .header-anchor:focus-visible{outline:none}h1 .header-anchor:focus-visible:before,h2 .header-anchor:focus-visible:before,h3 .header-anchor:focus-visible:before,h4 .header-anchor:focus-visible:before,h5 .header-anchor:focus-visible:before,h6 .header-anchor:focus-visible:before{content:"¶";position:absolute;left:-.75em;color:var(--c-brand);outline:auto}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");background-position:center;background-repeat:no-repeat;line-height:normal;transition:all .3s}html.dark .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.arrow.down{transform:rotate(180deg)}.arrow.right{transform:rotate(90deg)}.arrow.left{transform:rotate(-90deg)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-title);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--c-code-group-tab-bg)}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--c-code-group-tab-title);font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid var(--c-code-group-tab-outline)}.code-group__nav-tab-active{border-bottom:var(--c-code-group-tab-active-border) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:not(.header-anchor):hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}#vp-comment{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem}@media (max-width: 959px){#vp-comment{padding:2rem}}@media (max-width: 419px){#vp-comment{padding:1.5rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.route-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}:root{--c-brand: #2176d6;--c-brand-light: #00c3ff}html.dark{--c-brand: #2176d6;--c-brand-light: #00c3ff}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}@media print{.search-box{display:none}}.search-box input{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url("data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='13'%3e%3cg%20stroke-width='2'%20stroke='%23aaa'%20fill='none'%3e%3cpath%20d='M11.29%2011.71l-4-4'/%3e%3ccircle%20cx='5'%20cy='5'%20r='4'/%3e%3c/g%3e%3c/svg%3e") .6rem .5rem no-repeat;background-size:1rem}@media (max-width: 719px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}@media (max-width: 719px){.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 419px){.search-box input:focus{width:8rem}}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}@media (max-width: 419px){.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}@keyframes rotate{0%{transform:rotate(0)}50%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.popup-enter-active,.popup-leave-active{transition:opacity .3s,transform .3s}.popup-enter-from,.popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}.sw-hint-popup,.sw-update-popup{position:fixed;bottom:1rem;inset-inline-end:1rem;z-index:var(--pwa-z-index);padding:.5rem .75rem;border-width:0;border-radius:.5rem;background:var(--pwa-bg-color);color:var(--pwa-color);box-shadow:0 2px 12px 0 var(--pwa-shadow-color);font-size:1rem;line-height:1.5;cursor:pointer}@media print{.sw-hint-popup,.sw-update-popup{display:none}}.sw-hint-popup .icon-wrapper,.sw-update-popup .icon-wrapper{display:inline-block;vertical-align:middle;width:1.5rem;height:1.5rem;margin-inline-start:.4rem;border-radius:.75rem;background:var(--pwa-btn-bg-color)}.sw-hint-popup .icon-wrapper:hover,.sw-update-popup .icon-wrapper:hover{background:var(--pwa-btn-hover-bg-color)}.sw-hint-popup .icon-wrapper svg,.sw-update-popup .icon-wrapper svg{width:1.2rem;height:1.2rem;margin:.15rem;color:var(--pwa-btn-text-color);animation:rotate 3s ease infinite}:root{--pwa-z-index: 10;--pwa-color: #2c3e50;--pwa-bg-color: #ffffff;--pwa-border-color: #3eaf7c;--pwa-shadow-color: rgb(0 0 0 / 15%);--pwa-btn-text-color: #ffffff;--pwa-btn-bg-color: #3eaf7c;--pwa-btn-hover-bg-color: #4abf8a;--pwa-content-color: #333;--pwa-content-light-color: #666}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}html.dark{--box-shadow: #0f0e0d;--card-shadow: rgba(0, 0, 0, .3);--black: #fff;--grey-dark: #999;--grey-light: #666;--white: #000;--grey-darker: #bbb;--grey-lighter: #333;--grey14: #111}:root{--vp-bg: var(--c-bg, #fff);--vp-bgl: var(--c-bg-light, #f3f4f5);--vp-bglt: var(--c-bg-lighter, #eeeeee);--vp-c: var(--c-text, #2c3e50);--vp-cl: var(--c-text-light, #3a5169);--vp-clt: var(--c-text-lighter, #4e6e8e);--vp-brc: var(--c-border, #eaecef);--vp-brcd: var(--c-border-dark, #dfe2e5);--vp-tc: var(--c-brand, #3eaf7c);--vp-tcl: var(--c-brand-light, #4abf8a);--vp-ct: var(--t-color, .3s ease);--vp-tt: var(--t-transform, .3s ease);--box-shadow: #f0f1f2;--card-shadow: rgba(0, 0, 0, .15);--black: #000;--grey-dark: #666;--grey-light: #999;--white: #fff;--grey-darker: #333;--grey-lighter: #bbb;--grey14: #eee}.theme-default-content figure{position:relative;display:flex;flex-direction:column;width:auto;margin:1rem auto;text-align:center;transition:transform var(--vp-tt)}.theme-default-content figure img{overflow:hidden;margin:0 auto;border-radius:8px}.theme-default-content figure img[tabindex]:hover,.theme-default-content figure img[tabindex]:focus{box-shadow:2px 2px 10px 0 var(--card-shadow)}@media print{.theme-default-content figure>a[href^="http://"]:after,.theme-default-content figure>a[href^="https://"]:after{content:""}}.theme-default-content figure>a .external-link-icon{display:none}.theme-default-content figure figcaption{display:inline-block;margin:6px auto;font-size:.8rem}html:not(.dark) figure:has(img[data-mode=darkmode-only]),html:not(.dark) img[data-mode=darkmode-only]{display:none!important}html.dark figure:has(img[data-mode=lightmode-only]),html.dark img[data-mode=lightmode-only]{display:none!important} diff --git a/assets/swftui-playground-MsNlfoqb.png b/assets/swftui-playground-MsNlfoqb.png new file mode 100644 index 0000000..e49f618 Binary files /dev/null and b/assets/swftui-playground-MsNlfoqb.png differ diff --git a/assets/swiftui-framework-wwdc-OMsLBsB_.jpg b/assets/swiftui-framework-wwdc-OMsLBsB_.jpg new file mode 100644 index 0000000..d673c12 Binary files /dev/null and b/assets/swiftui-framework-wwdc-OMsLBsB_.jpg differ diff --git a/assets/xcode-new-project-BgcBHlMK.png b/assets/xcode-new-project-BgcBHlMK.png new file mode 100644 index 0000000..5e6a509 Binary files /dev/null and b/assets/xcode-new-project-BgcBHlMK.png differ diff --git a/assets/xcodes-DaBHvxPG.png b/assets/xcodes-DaBHvxPG.png new file mode 100644 index 0000000..c168fea Binary files /dev/null and b/assets/xcodes-DaBHvxPG.png differ diff --git a/browserconfig.xml b/browserconfig.xml new file mode 100644 index 0000000..72dcbee --- /dev/null +++ b/browserconfig.xml @@ -0,0 +1,11 @@ + + + + + + + + #ffffff + + + \ No newline at end of file diff --git a/favicon-16x16.png b/favicon-16x16.png new file mode 100644 index 0000000..1e94c2e Binary files /dev/null and b/favicon-16x16.png differ diff --git a/favicon-32x32.png b/favicon-32x32.png new file mode 100644 index 0000000..8fdc5f7 Binary files /dev/null and b/favicon-32x32.png differ diff --git a/favicon-96x96.png b/favicon-96x96.png new file mode 100644 index 0000000..66c2062 Binary files /dev/null and b/favicon-96x96.png differ diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..e2bcc48 Binary files /dev/null and b/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..2291df1 --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Welcome | iOS Training + + + + + +
    iOS Training

    iOS Training

    Getting started with iOS development with Swift and SwiftUI

    Get started →

    Swift

    Discover the Swift programming language (version 5.10)

    SwiftUI

    Develop iOS applications

    + + + diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..2822660 Binary files /dev/null and b/logo.png differ diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000..c9478c0 --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1 @@ +{"name":"Worldline iOS training","short_name":"iOS Training","description":"iOS developer beginner training","lang":"en-US","start_url":"index.html","scope":"/ios-training/","display":"standalone","theme_color":"#46bd87","background_color":"#ffffff","orientation":"portrait-primary","prefer_related_applications":false,"icons":[{"src":"android-icon-36x36.png","sizes":"36x36","type":"image/png","density":"0.75"},{"src":"android-icon-48x48.png","sizes":"48x48","type":"image/png","density":"1.0"},{"src":"android-icon-72x72.png","sizes":"72x72","type":"image/png","density":"1.5"},{"src":"android-icon-96x96.png","sizes":"96x96","type":"image/png","density":"2.0"},{"src":"android-icon-144x144.png","sizes":"144x144","type":"image/png","density":"3.0"},{"src":"android-icon-192x192.png","sizes":"192x192","type":"image/png","density":"4.0"}]} diff --git a/mini-project/index.html b/mini-project/index.html new file mode 100644 index 0000000..7f3e94e --- /dev/null +++ b/mini-project/index.html @@ -0,0 +1,120 @@ + + + + + + + + + Mini project | iOS Training + + + + + +

    Mini project

    The final chapter of this training will ask you to create a SwiftUI app from scratch.

    Requirements

    The app consists of a movie explorer app with the following features:

    • Search for movies by title.
    • View the details of the selected movie.
    • The app requires the user to be logged in.
    • The app allows a new user to register.
    • The movie list screen allows to logout from the app.
    • The app remembers the logged in user after a restart.
    • The app uses this APIopen in new window for the authenticating and searching for movies.
      • The /movies/search endpoint requires to pass the token retrieved from endpoint /user/login or user/register in this header: Authorization: Bearer \(userResponse.token)
    • (Optional) The result of previous queries is locally cached.
    • (Optional) Add movie to local favorites ⭐️
    • (Optional) Animate the transition between the login view and the movie list view (tutorialopen in new window).

    A preview of the app can be seen here.

    Hints

    • There are many techniques to handle the flow from the login view to the movie list view. On of them is to rely on a logged state. The following gives an overview how it looks like.
    struct ContentView: View {
    +    @State var loggedIn: false
    +    
    +    var body: some View {
    +        if loggedIn {
    +            MovieListView()
    +        } else {
    +            // The LoginView takes a callback that is called when the login succeeds
    +            LoginView { newLoggedIn in
    +                loggedIn = newLoggedIn
    +            }
    +        }
    +    }
    +}
    +
    • In the login view, use an enum to track the state of the login operation so that you can disable the login button when a request is running.
    enum LoginState {
    +    case neutral, loading, success, failure
    +}
    +struct LoginView: View {
    +    @State private var loginState: LoginState = .neutral
    +    // other code
    +}
    +
    • Use a Task object to run async code.
    Button("Login") { 
    +    loginState = .loading
    +    Task {
    +        if await login() {
    +            onLoginSuccess(true)
    +        }
    +    }
    +}
    +

    Swift Concurrency crashes on Swift Playground

    Do not use the Swift Playground app to run you app as it does not work well with SwiftUI + Swift Concurrency (async, await and Task). Instead, you can create an Xcode project of type Playground to combine the power of Xcode and the simplicity of Playground projects.

    • Use DebouncedOnChangeopen in new window Swift package to optimize search.
    • To generate the initial code for a preview, open a view and then use the Xcode feature Editor -> Create preview
    • The List view requires that you specify an id field List(movies, id: \.title) or that the items conform to Identifiable protocol
    • If you can't add SwiftPM packages from Xcode, add them by editing the package.swift file by hand. Here is an example below.
    // swift-tools-version: 5.6
    +
    +// WARNING:
    +// This file is automatically generated.
    +// Do not edit it by hand because the contents will be replaced.
    +
    +import PackageDescription
    +import AppleProductTypes
    +
    +let package = Package(
    +    name: "Moovy",
    +    platforms: [
    +        .iOS("15.2"),
    +        .macOS("13.0")
    +    ],
    +    products: [
    +        .iOSApplication(
    +            name: "Moovy",
    +            targets: ["AppModule"],
    +            displayVersion: "1.0",
    +            bundleVersion: "1",
    +            appIcon: .placeholder(icon: .sun),
    +            accentColor: .presetColor(.indigo),
    +            supportedDeviceFamilies: [
    +                .pad,
    +                .phone
    +            ],
    +            supportedInterfaceOrientations: [
    +                .portrait,
    +                .landscapeRight,
    +                .landscapeLeft,
    +                .portraitUpsideDown(.when(deviceFamilies: [.pad]))
    +            ],
    +            capabilities: [
    +                .outgoingNetworkConnections()
    +            ],
    +            appCategory: .entertainment
    +        )
    +    ],
    +    dependencies: [
    +        .package(url: "https://github.com/Tunous/DebouncedOnChange.git", "1.0.0"..<"2.0.0"),
    +        .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", "4.0.0"..<"5.0.0")
    +    ],
    +    targets: [
    +        .executableTarget(
    +            name: "AppModule",
    +            dependencies: [
    +                "DebouncedOnChange",
    +                "KeychainAccess"
    +            ],
    +            path: "."
    +        )
    +    ]
    +)
    +
    + + + diff --git a/ms-icon-144x144.png b/ms-icon-144x144.png new file mode 100644 index 0000000..608eafc Binary files /dev/null and b/ms-icon-144x144.png differ diff --git a/ms-icon-150x150.png b/ms-icon-150x150.png new file mode 100644 index 0000000..9485f03 Binary files /dev/null and b/ms-icon-150x150.png differ diff --git a/ms-icon-310x310.png b/ms-icon-310x310.png new file mode 100644 index 0000000..a686259 Binary files /dev/null and b/ms-icon-310x310.png differ diff --git a/ms-icon-70x70.png b/ms-icon-70x70.png new file mode 100644 index 0000000..1109ef7 Binary files /dev/null and b/ms-icon-70x70.png differ diff --git a/persist-data/index.html b/persist-data/index.html new file mode 100644 index 0000000..6ed80e4 --- /dev/null +++ b/persist-data/index.html @@ -0,0 +1,39 @@ + + + + + + + + + Locally persisting data | iOS Training + + + + + +

    Locally persisting data

    Estimated time

    1/4 day

    Persisting data locally consists of keeping app data after the app has been killed and the variables removed from memory. The persisted data offers many advantages. We can use it to show initial data when the app starts and waits for the first batch data to be fetched from the server. It can also be used to allow for offline app usage.

    iOS isolates app data from other apps

    For security reasons, each app is isolated from the rest of the apps. This is called sandboxing. This articleopen in new window shows the different ways that allow two or more apps to share their data

    There are many ways to persist data in SwiftUI that we cover below.

    UserDefaults

    It is a very simple key-value storage that persists data in a file. The API surface is very small and the developer does not need to manage the persisted file. This makes this technique very efficient for simple storage use cases. You can find a short guide hereopen in new window.

    Here is sample code that shows how to persist and load data.

    UserDefaults.standard.set(self.tapCount, forKey: "Tap")
    +@State private var tapCount = UserDefaults.standard.integer(forKey: "Tap")
    +

    Codable saved in a file

    A more advanced and powerful technique is to manually load and persist a Codable into a file. This technique is useful if you want to store complex objects (such as the state or model) in a JSON file. There are two steps in this process, the first one consists of decoding / encoding the object from / into JSON using JSONDecoder().decode and JSONEncoder().encode. The second step consists of loading / saving the encoded data and we can think of two ways to achieve this. The first one consists of user defaults' dataForKey: to load the data and setObject:ForKey to persist it. Another one consists of creating and managing a file by the developer using file APIs such as fileHandle.availableData to load the data from a file and data.write to save it.

    Sophisticated data persistence libraries

    For storing data in a database or similar fashion, SQLite is available as a low level library. It is not recommended to use it unless there is a strong performance concern. Instead, it is recommended to use libraries specialized in data persistence. Some can be assimilated to an ORM library (Object Relational Mapper). The remainder of this section describes some of them.

    Please be careful about the pricing of cloud storage

    Sophisticated databases generally provide cloud storage to provide a complete offer. If you're interested in storing data in the cloud, please take some time to read the pricing page to avoid any bad surprises when your app runs in production.

    Core Data

    Core Dataopen in new window is the official library to "Persist or cache data on a single device, or sync data to multiple devices with CloudKit". It existed since iOS 3 and Apple continuously updates it to keep it relevant. It also has the reputation of having a steep learning curve, but it remains famous among developers.

    It works similarly as an ORM where classes are mapped into tables. Xcode provides a graphical editor that allows to specify the tables, the relations and generate the necessary code (in Swift or Objective-C).

    Core date editor
    Core date editor

    Even though Core Date existed before SwiftUI, Apple made sure that both of them can be used together. This article shows how to use Core Data in a SwiftUI projectopen in new window.

    Realm

    Realmopen in new window is a high level alternative to SQLite. It can be seen as alternative to Core Data as they seem to provide a similar list of features. Most notably, the possibility to store data locally or in the cloud. The points where Realm wins is that the library seems simpler to learn and to use and that it is also available in Android.

    Firebase datastore (or any other cloud based storage)

    As opposed to Realm and Core Data, which are local first databases, Firebase datastore is a cloud first database. This means that Firebase Datastore requires an internet connection to store and load the data. However, the library is simple to use and supports real time updates.

    TIP

    Firebase datastore is part of a bigger suite of service called Firebase. For example, we can Firebase App Distribution in Firebase, which is a service that allows to deploy and distribute apps without going the burden of using TestFlight.

    PW: complete the official iOS persisting data tutorial

    This PW shows how to save a Codable in a manually managed file using JSON encoder and filesystem APIs.

    https://developer.apple.com/tutorials/app-dev-training/persisting-dataopen in new window

    + + + diff --git a/presentation/index.html b/presentation/index.html new file mode 100644 index 0000000..e605226 --- /dev/null +++ b/presentation/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Presentation | iOS Training + + + + + +

    Presentation

    Welcompe the world of iOS development

    iOS development consists of developing applications that can target mainly the iPhone and iPad but also macOS, iWatch and Apple TV. There are many ways to achieve this:

    • Using the official frameworks and tools provided by Apple
    • Using 3rd party frameworks and tools such as Capacitor, MAUI and Flutter

    This training focuses on iOS development using the official tools and frameworks proposed by Apple. Our development stack will consist of the following items:

    • Programming language: Swift
    • UI Framework: SwiftUI
    • IDEs: Xcode and Swift Playgrounds

    In addition to that, It is also possible to leverage the Swift language (without SwiftUI) in order to develop console apps and servers on Window, Linux, macOS.

    History

    The early days of iOS development used the Objective-C language, the UIKit UI Framework and -the good old- Xcode. This ecosystem was basic but quite powerful and allowed to develop amazing apps. The continuous updates from Apple improved the developer experience. For example, memory management became automatic (thanks to ARC) and the layout system became capable of adapting to different screen sizes.

    In WWDC 2014, Apple announced the Swift language as an Open Source modern replacement to Objective-C. Following that, apple announced during the next WWDC SwiftUI as the replacement for UIKit.

    swiftui
    swiftui

    As of 2021, the majority of new iOS projects use Swift and SwiftUI with UIKit as a fallback for the UI aspects.

    Getting started

    xcodes
    xcodes

    Create a CLI app with swift CLI

    • Open a terminal in an empty folder mkdir MyCLI and then cd MyCLI
    • Create the project with swift package init --name MyCLI --type executable
    • and run it with swift run. It should print -> Hello world

    Create an App using Xcode or Swift playgrounds

    • On Xcode, create a either a Project or Playground and run it.
    xcode-new-project.png
    xcode-new-project.png

    Swift project managers

    In this training, we'll use swiftpm and Xcode.

    + + + diff --git a/service-worker.js b/service-worker.js new file mode 100644 index 0000000..1dc4742 --- /dev/null +++ b/service-worker.js @@ -0,0 +1 @@ +if(!self.define){let e,s={};const i=(i,t)=>(i=new URL(i+".js",t).href,s[i]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()})).then((()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(t,r)=>{const d=e||("document"in self?document.currentScript.src:"")||location.href;if(s[d])return;let n={};const l=e=>i(e,d),c={module:{uri:d},exports:n,require:l};s[d]=Promise.all(t.map((e=>c[e]||l(e)))).then((e=>(r(...e),n)))}}define(["./workbox-1ab968a5"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.clientsClaim(),e.precacheAndRoute([{url:"assets/404.html-9pEcDQrh.js",revision:"ec8d0613063a8efe33850b20ea9a86d7"},{url:"assets/app-Bbun9eEO.js",revision:"2065692954e7cf20d865050915113c60"},{url:"assets/index-DTEEl-sV.js",revision:"46a193641571106d3b7b43f9bc2a2735"},{url:"assets/index.html-5n7MuywO.js",revision:"009154f00c5932eb724eb96396250ae4"},{url:"assets/index.html-B8dggsdP.js",revision:"198774e301bf751241b4bb9b9f9153bd"},{url:"assets/index.html-C39pB5w2.js",revision:"89a0fdb527a8b6f74b08e2d90c4d9f7e"},{url:"assets/index.html-CfxHtkWF.js",revision:"583b5fe12bc77bb25db79e451d5d9a24"},{url:"assets/index.html-ChAAQVq_.js",revision:"d91b5994055d7f83023b05963bde6214"},{url:"assets/index.html-Cv2fRjYf.js",revision:"5a1ac78a3cb7017fe5cce5468a70b83d"},{url:"assets/index.html-DKuyFSlS.js",revision:"60b6fbeb32a391781b5cf778b54c1b11"},{url:"assets/index.html-DOlN3MeL.js",revision:"99fd78a922e4c8dc345cb6df332af5f7"},{url:"assets/index.html-z4YYCP43.js",revision:"1ff4fd171aed54781d715261e048ef51"},{url:"assets/style-DGf4msjw.css",revision:"8230bc058d85c85b89623d99eba76ed6"},{url:"index.html",revision:"4f4c745857f8b16c96faccbda6b001f5"},{url:"404.html",revision:"e90035ac2e19c4100209c50a1c28c1af"}],{}),e.cleanupOutdatedCaches()})); diff --git a/styles.scss b/styles.scss new file mode 100644 index 0000000..155d241 --- /dev/null +++ b/styles.scss @@ -0,0 +1,191 @@ +/*! + * Forked from Writ v1.0.4 + * Copyright © 2015, Curtis McEnroe + * https://cmcenroe.me/writ/LICENSE (ISC) + */ + +/* Fonts, sizes & vertical rhythm */ + +html { + font-family: Palatino, Georgia, Lucida Bright, Book Antiqua, serif; + font-size: 16px; + line-height: 1.5rem; +} + +h1, h2, h3, h4, h5, h6, th { font-weight: normal; } + +/* Minor third */ +h1 { font-size: 2.488em; } +h2 { font-size: 2.074em; } +h3 { font-size: 1.728em; } +h4 { font-size: 1.44em; } +h5 { font-size: 1.2em; } +h6 { font-size: 1em; } +small { font-size: 0.833em; } + +h1, h2, h3 { line-height: 3rem; } + +p, ul, ol, dl, table, blockquote, pre, h1, h2, h3, h4, h5, h6 { + margin: 1.5rem 0 0; +} +ul ul, ol ol, ul ol, ol ul { margin: 0; } + +hr { + margin: 0; + border: none; + padding: 1.5rem 0 0; +} + +/* Colors */ + +body { color: #222; background-color: #fcfcfc; } +code, pre, samp, kbd { color: #111; } +a, header nav a:visited, a code { color: #00e; } +a:visited, a:visited code { color: #60b; } +mark { color: inherit; } + +code, pre, samp, thead, tfoot { background-color: rgba(0, 0, 0, 0.05); } +mark { background-color: #fe0; } + +main aside, blockquote, ins { border: solid rgba(0, 0, 0, 0.05); } +pre, code, samp { border: solid rgba(0, 0, 0, 0.1); } +th, td { border: solid #dbdbdb; } + +/* Layout */ + +body { margin: 1.5rem 1ch; } + +body > header { text-align: center; } + +main, body > footer { + display: block; /* Just in case */ + max-width: 78ch; + margin: auto; +} + +img { max-width: 100%; } + +/* Lists */ + +ul, ol, dd { padding: 0 0 0 3ch; } +dd { margin: 0; } + +nav ul { + padding: 0; + list-style-type: none; +} +nav ul li { + display: inline; + padding-left: 1ch; + white-space: nowrap; +} +nav ul li:first-child { padding-left: 0; } + +/* Tables */ + +table { + width: 100%; + border-collapse: collapse; + overflow-x: auto; +} + +th, td { + border-width: 1px; + padding: 0 0.5ch; +} + +/* Copy inline */ + +a { text-decoration: none; } + +mark { + padding: 1px; +} + +code, samp { + border-width: 1px; + border-radius: 2px; + padding: 0.1em 0.2em; + white-space: nowrap; +} + +form, .card { + background: white; + box-shadow: 0 0 25px rgba(0,0,0,0.5); + padding: 0.5em; + margin: 1em 0; +} + +button, input { + font-size: 1.2rem; + margin: 0.5em; + padding: 0.25em 0.5em; +} + +dl { + padding: 0; + display: flex; + flex-wrap: wrap; +} + +ul { + padding: 0; + display: flex; + flex-flow: column nowrap; + align-items: center; + justify-content: flex-start; + flex-wrap: wrap; +} + +li { + list-style: none; +} + +dt, label { + display: inline-block; + width: 30%; + text-align: right; + margin-right: 1em; + font-weight: bold; +} + +dd, input { + display: inline-block; + min-width: 40%; + flex: 1; + text-align: left; +} + +#login-form { + display: flex; + justify-content: center; +} + +#logout-btn { + position: fixed; + right: 1em; + top: 1em; +} + +.film.card { + width: 960px; +} + +.film .poster { + float: left; + margin: 0 0.5em 0.5em 0; +} + +.film .title { + font-size: 2rem; +} + +.rating { + color: gold; + text-shadow: 1px 1px 1px rgba(0,0,0,0.5) +} + +app-film { + display: flex; + width: 960px; +} \ No newline at end of file diff --git a/swift-part1/index.html b/swift-part1/index.html new file mode 100644 index 0000000..bd548b5 --- /dev/null +++ b/swift-part1/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Swift (part 1) | iOS Training + + + + + +

    Swift (part 1)

    Estimated time

    1/2 day

    Swift is the official programming language for developing iOS, iPadOS, macOS, watchOS and AppleTV apps. It can also target other platforms such as Windows, Linux and Android.

    The source code of the language toolchain is hosted in swiftlang/swiftopen in new window GitHub repository.

    A quick tour of some features

    Swift has modern and interesting features. Here are some notable ones:

    • Swift is statically typed and supports implicit typing.
      • Static typing: types cannot change on runtime (it is the opposite of dynamic typing).
      • Implicit typing: the compiler can infer the type whenever possible.
    • var creates mutable variables.
    • let creates immutable variables or constants.
    • String interpolation is available with this syntax \(expression).
    • Parenthesis are not required in if, for, while and switch statements.
    • if and switch statements are expressions.
    • for-each is the only type of for loop available.
    • Optionals allows to write code free from null pointer errors (also called Null Safety in other languages).
    • Functional programming is supported (Higher-order functions and functions as 1st class items, etc.).
    • Object oriented programming is supported.
    • Interfaces are called protocols and they are used a lot.
    • Structures are available and provide a lot of features (More on that later).

    this codeopen in new window illustrates some of the features listed above.

    In the following sections, we will delve into more features.

    ++ and -- are removed since swift 3

    This postopen in new window details all the problems related to using these operators.

    Functions

    In the this section, the terms argument and parameter are used interchangeably.

    The declaration of functions in Swift has the following peculiarities:

    • Parameters are named and ordered. This means that when you call a function, you must specify the name of the arguments in the same order as the declaration.
    • A parameter can have different external and internal names by declaring it like this: externalName internalName: Type. The external name is also called an argument label.
    • You can make a parameter anonymous by setting this external name: _.
    • Arguments can have a default value. These are also called optional arguments.

    This codeopen in new window illustrates the above features.

    Swift allows to use functions as first class items or citizens. This allows to store function references into variables, pass functions as arguments to other functions and return a function from a function. Here is a brief listing of the these features:

    • A function can be assigned to a variable, passed as a function parameter or returned from a function.
    • A function type can be expressed as follows: (typeOfParam1, typeOfParam2, etc) -> returnType.
    • The empty return type is Void.
    • We can use typealias to shorten writing long types.
    • Swift supports anonymous functions (also called lambda function) with the following syntax { argName1, argName2, etc. in // code }

    This codeopen in new window illustrates these features.

    Let's explore in the next section, one of the most amazing features of Swift which is Optionals.

    Optionals (aka. Null safety)

    In a nutshell, optionals is a compiler feature that allows you to avoid the infamous Null pointer exception or npe. The Swift compiler provides null safety and reports errors and warnings when we manipulate nullable (also called optional) values. Here is a list of null safety features provided by swift:

    The name of null in iOS development

    In Swift, the null value is called nil

    • All types are non optional by default. This means that we cannot assign nil to a variable or an argument. For example, this code fails var s: String = nil.
    • A type can be made optional by suffixing it with a ?. For example: var s: String? = nil.
    • You cannot call a method or a property of an optional type, unless you do one of those possibilities:
      • Use optional chaining with the ? suffix.
      • Provide a default value with the ?? operator.
      • Unwrap the optional so that it becomes non optional.
      • Force unwrap the optional using the ! suffix. This should never be used as it bypasses compiler checks.

    Never unwrap with !

    You must never force unwrap with the !. Use other unwrapping techniques instead. On of the rarest exceptions is with Interface builder's Outlets in UIKit @IBOutlet var label: UILabel!. Fortunately, since we are not using UIKit in this training, we will avoid this situation.

    This codeopen in new window illustrates null safety and how to use optional types.

    Enumerations

    Enumerations allow to work with a group of values in a type-safe fashion. Swift provides many interesting features to enumerations:

    • When the compiler can infer it, you can omit the name of the enumeration when you use one of its values.
    • Switch statements support enumerations.
    • You can easily iterate over an enum's values by using : CaseIterable.
    • You can associate values or provide a raw value to enumeration cases. Raw values can be implicitly assigned.
    • You can use another enumeration as associated value, this is called recursive enumeration.

    This codeopen in new window illustrates some enumeration features. For further reading please consult the official documentationopen in new window.

    Error management

    Swift provides two ways for error management: Exceptions and the Result type.

    • Exceptions provide an alternate return route with the throw [value] keyword.
      • The thrown value must conform the Error protocol. We can even throw a Stringopen in new window that way.
      • We must call throw when we want to return an error. Throwing in a normal situation is a bad practice.
      • We say that a function throws when it can throw and exception. It must have the throws qualifier.
      • When we call a function that throws, we must precede the call with try keyword
      • When we call a function that throws, we can either propagate its error if it is thrown or handle to stop its propagation.
    • The Result type is a an enum that has two possible cases: success(Sucess) or failure(Failure)
      • The failure value must conform to the Error protocol
      • A Result can be handled with usual Swift features for enums: guard, switch, etc.
      • The Result type has can be used with the exception style. Its get() method returns the success value or throws the error.

    This codeopen in new window illustrates error handling features.

    Some features in bulk

    Exercises

    Exercise 1

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 2

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 3

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Sources

    + + + diff --git a/swift-part2/index.html b/swift-part2/index.html new file mode 100644 index 0000000..2e68240 --- /dev/null +++ b/swift-part2/index.html @@ -0,0 +1,75 @@ + + + + + + + + + Swift (part 2) | iOS Training + + + + + +

    Swift (part 2)

    Estimated time

    1/2 day

    Object oriented programming features

    Swift supports most Object Oriented Programming features:

    • Classes that can be instantiated into objects.
      • Constructors and destructors are called initializers and deinitializers respectively.
    • Encapsulation and 4 access levelsopen in new window that range from private to public
    • Simple inheritance of classes. Multiple inheritance of classes and is not supported.
    • Inheritance allows one class to use the characteristics of another.
    • Method overriding and polymorphismopen in new window and access control.
    • Overloading of operators and functions, compositionopen in new window.
    • Static methods and properties are supported.
    • Generic types are supported
    • Protocols which are the equivalent of Interfaces.
      • Classes and structs can conform to multiple protocols.
      • Protocols can have associated typesopen in new window which is similar to generic types.
      • They are used a lot by swift developers to the point that there is a programming technique called Protocol oriented programming.

    Here are some additional features:

    • Extensions allow to add functions and conform to additional protocols outside of the original class, struct or protocol declaration. This has many uses that simplify our code and here are some examples.
      • They can add methods to classes from any library that we can use.
      • They can define default implementations in protocols.
    • Abstract classes are not available

    this codeopen in new window illustrates some of the above features.

    In additions to classes, structs in swift are powerful and provide similar features than classes with some exceptions.

    Structs

    In Swift, structs have many similar features with classes. They support properties, methods, subscriptsopen in new window, initializers, extensions and conforming to protocols. The features that are only available in classes are as followsopen in new window:

    • Inheritance.
    • Type casting (enables you to check and interpret the type of a class instance at runtime).
    • Deinitializers.
    • Reference counting allows more than one reference to a class instance (similar to pointers but much less complex to use).

    this codeopen in new window sample shows how to use structs with protocols.

    Opaque types

    This feature seems advanced to understand but since it's used a lot in SwiftUI, let's explore a simple explanation and we'll provide some links to study it further.

    In a base level, opaque types allow to return Protocols while keeping the concrete type information known by the compiler. It is enabled by prefixing the type with the some keyword.

    Opaque types allow to keep the benefits of abstracting the code on a developer level while maintaining the performance and optimization benefits of concrete typing. In addition to that, they allow the compiler to better handle some cases such as Self or associated type requirements. Please note that explaining all the features that opaque types bring to the code is an advanced topic. For more information and details, please read the articles mentioned in the Sources and more reading section.

    For this training, we'll assume that opaque help types the compiler perform better optimizations with protocols, are used in many places in SwiftUI and allow to improve our code in some cases. We'll show below a simple use case where we can define a method that returns an Equatable.

    // Source: https://www.educative.io/answers/what-is-opaque-type-in-swift
    +
    +// create a function that returns some Equatable
    +// The compiler fails is the return type is just "Equatable"
    +func makeInteger() -> some Equatable{
    +  Int.random(in: 0...10)
    +}
    +
    +let firstInteger = makeInteger()
    +let secondInteger = makeInteger()
    +
    +// this returns "false" because they are of the same concrete type else, Xcode will scream at us.
    +print(firstInteger == secondInteger)
    +
    +func makeString() -> some Equatable{
    +  "A String"
    +}
    +let firstString = makeString()
    +
    +// Compiler error because the concrete type is not the same.
    +print(firstInteger == firstString)
    +

    As of Swift 5.1 opaque types are only available for return valuesopen in new window. As of Swift 5.7 opaque arguments have been implementedopen in new window

    Use structs by default

    As surprising as it seems, Apple recommends using structs by default instead of classesopen in new window. More precisely, when we want to add a new data type, we should not assume that it should be a class by default and check if a structure is more relevant. Apple provides the following recommendations:

    • Use structures by default.
    • Use classes when you need Objective-C interoperability.
    • Use classes when you need to control the identity of the data you’re modeling.
    • Use structures along with protocols to adopt behavior by sharing implementations.

    We note that structures are the default choice mostly because they are value types. This makes the code more predictable because changes cannot come from a parent call. Another advantage of structs is that they are more friendly with functional programming. We'll talk about functional programming in the next section.

    Functional programming features

    Functional programming revolves around three main conceptsopen in new window: pure functions, immutable objects and declarative programming.

    Pure functions are functions that do not have side effects and will thus return always the same output given the same input. Swift allows to create pure functions but does not provide compile time guarantees that a function is pure.

    Immutable objects can be created using classes or structs with constant properties (declared with let). As mentioned above, structs are recommended by default and here are other good reasonsopen in new window. One of the most notable ones is that since structs are passed around by value, thus they help us avoiding side effects.

    Declarative programming can be easily explained as a way of programming that is centered around telling what to do and not how to do itopen in new window. This allows to obtain a clearer and more maintainable code than traditional imperative programming. For example, when we want to filter a table, a for loop is not declarative (we say imperative in this case) while the WHERE SQL syntax is considered declarative. Declarative programing is possible in Swift through chaining functions and passing functions as arguments. Indeed, as we have seen earlier, Swift has 1st class support for functions. In addition to that, we can find declarative APIs in the standard Swift library and in Swift UI. The latter will be explored in a different chapter. For now, let's illustrate with this codeopen in new window how to process a list of strings using only declarative APIs provided by Swift.

    Swift has many more features and provides a rich standard library. We'll explore them as needed in the next sections. For now, let's create some UIs in the next chapter.

    Structured Concurrency

    • Swift supports writing concurrent code in a structured way.
    • Concurrency means that we execute multiple tasks at the same time. For example, update the UI while the app performs an HTTP request to the server.
    • In Swift, we can create concurrent Tasks with the Task, TaskGroup types.
    • Without Structured concurrency, we would use complex concepts to such as callbacks which make code less readable (have you lived the callback hellopen in new window ?).
    • We say that a code is structuredopen in new window when we use the well-know control flow structures :if/then/else, loops, functions, lexical scopes for variables.
      • Structured concurrency means that we write concurrent code using the usual control flow structures (as opposed to callback-based concurrent code)
      • In Swift is possible through the async and await keywords.
      • When we await a Task, the control flow will continue when it end without blocking the Task or TaskGroup on which it is launched.
      • A function that has uses the await keyword must be declared as async
    • To summarize async and await + Task and TaskGroup = Structured Concurrency
    • Continuations allow to convert callback code into async/await

    This swift script shows a sample of using Task + async/awaitopen in new window

    This swift script shows a sample of using TaskGoup + async/awaitopen in new window

    This swift script shows a sample of TaskGoup cancellationopen in new window

    This swift script shows how to convert callbacks into async/awaitopen in new window

    Structured Concurrency in Playground Book

    // Reference: https://stackoverflow.com/a/24066317
    +import PlaygroundSupport
    +
    +//Playground does not stop at the end of the code
    +PlaygroundPage.current.needsIndefiniteExecution = true
    +
    +func sampleFunc() async {
    +  print("sampleFunc")
    +  try? await Task.sleep(until: .now + .seconds(2))
    +}
    +
    +Task {
    +    await sampleFunc()
    +    print("done")
    +    // End the playground
    +    PlaygroundPage.current.finishExecution()
    +}
    +

    Generics

    • Generics allow to pass a type as a parameter to a class, struct, enum or function.
    • A type parameter can be declares with <T> where T is the type parameter.
    • Examples
      • func printArray<T>(array: [T]) { for item in array { print(item) } }
    • Swift can infer the type of the parameter if it is not provided and if it's not ambiguous.

    this codeopen in new window illustrates some of the above features.

    Key-paths

    • Key-pathsopen in new window allow to refer to properties of a type.
    • They are created with the \.propertyName syntax.
    • They are often used to sort, filter, group and map collections and in SwiftUI to bind properties to UI elements.

    this codeopen in new window illustrates some of the above features.

    Exercises

    Exercise 1

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 2

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 3

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solution 1open in new window

    Solution 2 with resultsopen in new window

    Sources and more reading

    + + + diff --git a/to-go-further/index.html b/to-go-further/index.html new file mode 100644 index 0000000..931902b --- /dev/null +++ b/to-go-further/index.html @@ -0,0 +1,44 @@ + + + + + + + + + Going further | iOS Training + + + + + +

    Going further

    Server side development

    • Vapor is a Swift framework that allows to develop servers
    • Install the Vapor cli brew install vapor
    • Create a vapor project vapor new hello-vapor -n
    • Run the server: cd hello-vapor and swift run
    Building for debugging...
    +Build complete! (1.25s)
    +[ NOTICE ] Server starting on http://127.0.0.1:8080
    +
    • Test the server
    curl http://127.0.0.1:8080
    +It works!
    +➜ curl http://127.0.0.1:8080/hello
    +Hello, world!
    +

    Swift and SwoftUI on the browser

    Advanced Swift

    Conclusion

    This training was introduction on Swift and SwiftUI. It just scratched the surface of developing for the Apple development. There are many things that we didn't explore such as the accessibility, hardware features such as geolocation and the technical aspects of the developer account (certificates, provisioning profiles, etc.).

    To go further, it is advised to watch the videos from Apple's WWDCopen in new window (WorldWide Developer Conference - pronounced "dubdub dee cee"). There many other resources available online that you should pick and choose depending on the needs. Here are some of them:

    + + + diff --git a/ui-development/index.html b/ui-development/index.html new file mode 100644 index 0000000..2ac9394 --- /dev/null +++ b/ui-development/index.html @@ -0,0 +1,64 @@ + + + + + + + + + UI development | iOS Training + + + + + +

    UI development

    Estimated time

    1/2 day

    Apple provides two official UI frameworks : UIKit and SwiftUI.

    UIKit is the originally used framework for UI development. It relies on defining the UI in a separate file (storyboard or xib) and the behavior in a swift file. In 2019, Apple release the first version of SwiftUI.

    The remainder of this training focuses on SwiftUI.

    SwiftUI

    SwiftUI brings a new approach to build UIs that we can summarize as follows:

    • All the UI is defined in Swift code. Neither Storyboards nor xibs are needed anymore.
    • The UI is defined in a declarative style.
    • States and bindings allow to hold the app data. The app UI updates automatically when these data change.
    • UI elements are structs that conform to the View protocol.
    • Complex views can be defined by breaking them into smaller views. This is called view composition.
    • The modifier technique is used to apply modifications to a view. A modifier returns a new view each time.

    The official documentation of SwiftUI is available hereopen in new window.

    Prerequisites

    It is recommended to use Xcode to learn and create SwiftUI apps. For simple apps, we can use the Swift Playgroundsopen in new window app. There is a web playgroundopen in new window that can be exceptionally used. You can see a screenshot of the tool below.

    SwiftUI web playground
    SwiftUI web playground

    Another promising alternative to watch is compnerd's windows portopen in new window of UIKit and SwiftUI. So, if you can have a recent version Xcode running, this is be the best IDE for SwiftUI development.

    Anatomy of a basic view

    The following code shows a sample view.

    struct ContentView: View {
    +    var body: some View {
    +        VStack {
    +            Text("Hello SwiftUI")
    +                .font(.largeTitle)
    +                .foregroundColor(.blue)
    +                .padding()
    +            Button(action: {}) {
    +                HStack {
    +                    Image(systemName: "suit.heart.fill")
    +                        .foregroundColor(.red)
    +                    Text("I am a button")
    +                        .font(.headline)
    +                        .foregroundColor(.white)
    +                }
    +                .padding(12)
    +                .background(Color.orange)
    +                .cornerRadius(8)
    +            }
    +        }
    +    }
    +}
    +

    As noted earlier, SwiftUI views are structs that conforms to the View protocol. This protocol defined a computed property that returns a View as an opaque type.

    The body of the view has a VStack as its root element. A VStack is a container view that arrange its direct children vertically (on a column). The first child is a Text view and its second child is a Button.

    The Text view chains calls to some methods that we call modifiers. They allow to do anything that we want to the view that called it and they return a new View instance. This means that we can apply another modifier to the result of a modifier and so on (this is called chaining). This allows modifiers to have a declarative syntax that makes the code easy to understand. SwiftUI provides built-in modifiers and allows us to create custom ones. Can you match all the modifiers used in the code and their effects ?

    The modifiers used are:
    font(...)
    +foregroundColor(...)
    +padding(...)
    +background(...)
    +cornerRadius(...)
    +

    The button has no action, meaning that is does nothing on click and its content is defined as an HStack. An HStack is a container view that arrange its direct children horizontally (on a row). The stack contains an image and a button.

    The view renders as illustrated by the image below.

    Hello SwiftUI
    Hello SwiftUI

    Let's do more SwiftUI.

    A summary of important concepts

    • @State: Single source of truth of a view and should not be shared with other views.
    • @Binding: allows to pass a reference of a state to a child view using $state.
    • @EnvironmentObject: Allows to globally share data between views. An @EnvironmentObject conforms to the ObservableObject protocol and its properties have the @Published property wrapper.
    • @ObservedObject: Allows to observe changes in an object that conforms to the ObservableObject protocol.

    PW: complete some official SwiftUI tutorials

    Apple provides a comprehensive SwiftUI tutorialopen in new window that covers most of the basic use cases such as creating views and handling inputs, animations and transitions.

    Please cover these tutorials to get a good grasp of SwiftUI.

    + + + diff --git a/workbox-1ab968a5.js b/workbox-1ab968a5.js new file mode 100644 index 0000000..b46d3f7 --- /dev/null +++ b/workbox-1ab968a5.js @@ -0,0 +1 @@ +define(["exports"],(function(t){"use strict";try{self["workbox:core:7.0.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.0.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=r&&r.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:i})}catch(t){a=Promise.reject(t)}const h=r&&r.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const o=r.match({url:t,sameOrigin:e,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&0===i.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);const h={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},u=t=>[h.prefix,t,h.suffix].filter((t=>t&&t.length>0)).join("-"),l=t=>t||u(h.precache),f=t=>t||u(h.runtime);function w(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.0.0"]&&_()}catch(t){}function d(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class p{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class y{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.h.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.h=t}}let g;async function R(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=e?e(r):r,c=function(){if(void 0===g){const t=new Response("");if("body"in t)try{new Response(t.body),g=!0}catch(t){g=!1}g=!1}return g}()?i.body:await i.blob();return new Response(c,o)}function m(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class v{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const q=new Set;try{self["workbox:strategies:7.0.0"]&&_()}catch(t){}function U(t){return"string"==typeof t?new Request(t):t}class L{constructor(t,e){this.u={},Object.assign(this,e),this.event=e.event,this.l=t,this.p=new v,this.R=[],this.m=[...t.plugins],this.v=new Map;for(const t of this.m)this.v.set(t,{});this.event.waitUntil(this.p.promise)}async fetch(t){const{event:e}=this;let n=U(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const i=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const r=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.l.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:r,response:t});return t}catch(t){throw i&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:i.clone(),request:r.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=U(t);let s;const{cacheName:n,matchOptions:i}=this.l,r=await this.getCacheKey(e,"read"),o=Object.assign(Object.assign({},i),{cacheName:n});s=await caches.match(r,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:i,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(t,e){const n=U(t);var i;await(i=0,new Promise((t=>setTimeout(t,i))));const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(o=r.url,new URL(String(o),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var o;const c=await this.q(e);if(!c)return!1;const{cacheName:a,matchOptions:h}=this.l,u=await self.caches.open(a),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const i=m(e.url,s);if(e.url===i)return t.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,r);for(const e of o)if(i===m(e.url,s))return t.match(e,n)}(u,r.clone(),["__WB_REVISION__"],h):null;try{await u.put(r,l?c.clone():c)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of q)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:a,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.u[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=U(await t({mode:e,request:n,event:this.event,params:this.params}));this.u[s]=n}return this.u[s]}hasCallback(t){for(const e of this.l.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.l.plugins)if("function"==typeof e[t]){const s=this.v.get(e),n=n=>{const i=Object.assign(Object.assign({},n),{state:s});return e[t](i)};yield n}}waitUntil(t){return this.R.push(t),t}async doneWaiting(){let t;for(;t=this.R.shift();)await t}destroy(){this.p.resolve(null)}async q(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class b{constructor(t={}){this.cacheName=f(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,i=new L(this,{event:e,request:s,params:n}),r=this.U(i,s,e);return[r,this.L(r,i,s,e)]}async U(t,e,n){let i;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(i=await this._(e,t),!i||"error"===i.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const r of t.iterateCallbacks("handlerDidError"))if(i=await r({error:s,event:n,request:e}),i)break;if(!i)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))i=await s({event:n,request:e,response:i});return i}async L(t,e,s,n){let i,r;try{i=await t}catch(r){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:i}),await e.doneWaiting()}catch(t){t instanceof Error&&(r=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:i,error:r}),e.destroy(),r)throw r}}class C extends b{constructor(t={}){t.cacheName=l(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(C.copyRedirectedCacheableResponsesPlugin)}async _(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const i=e.params||{};if(!this.C)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=i.integrity,r=t.integrity,o=!r||r===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?r||s:void 0})),s&&o&&"no-cors"!==t.mode&&(this.k(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.k();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}k(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==C.copyRedirectedCacheableResponsesPlugin&&(n===C.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(C.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}C.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},C.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await R(t):t};class E{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.K=new Map,this.P=new Map,this.T=new Map,this.l=new C({cacheName:l(t),plugins:[...e,new y({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.l}precache(t){this.addToCacheList(t),this.W||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.W=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=d(n),r="string"!=typeof n&&n.revision?"reload":"default";if(this.K.has(i)&&this.K.get(i)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.K.get(i),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.T.has(t)&&this.T.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:i});this.T.set(t,n.integrity)}if(this.K.set(i,t),this.P.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return w(t,(async()=>{const e=new p;this.strategy.plugins.push(e);for(const[e,s]of this.K){const n=this.T.get(s),i=this.P.get(e),r=new Request(e,{integrity:n,cache:i,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return w(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.K.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.K}getCachedURLs(){return[...this.K.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.K.get(e.href)}getIntegrityForCacheKey(t){return this.T.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}let O;const x=()=>(O||(O=new E),O);class N extends i{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const i of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(r,e);if(yield o.href,s&&o.pathname.endsWith("/")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(i);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}function k(t){const e=x();!function(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new i((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)o=new r(t,e,n);else if("function"==typeof t)o=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}a().registerRoute(o)}(new N(e,t))}t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=l();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){x().precache(t)}(t),k(e)}}));