Phần 1 của loạt bài viết về CAShapeLayer đã nói về cách vẽ đường thẳng, các hình khối.
Ở phần 2 này, mình sẽ nói về những attribute quan trọng, thường được sử dụng của CAShapeLaye.
Content
Stroke
Fill Color
Line
Stroke
Stroke Color
strokeColor là thuộc tính để set màu cho đường line của 1 đường thẳng, hình khối.
Ex: Để vẽ 1 hình vuông có các đường thẳng màu xanh thì chỉ cần thêm dòng code dưới đây khi vẽ hình:
shapeLayer.strokeColor = UIColor.blue.cgColor
Code vẽ hình vuông:
func createSquare() {
let shapeLayer = CAShapeLayer()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0.0, y: 0.0))
path.addLine(to: CGPoint(x: 100.0, y: 0.0))
path.addLine(to: CGPoint(x: 100.0, y: 100.0))
path.addLine(to: CGPoint(x: 0.0, y: 100.0))
path.close() // creating a line segment between the first point and current point
shapeLayer.path = path.cgPath
shapeLayer.fillColor = nil
shapeLayer.strokeColor = UIColor.blue.cgColor
layer.addSublayer(shapeLayer)
}
Stroke Start & Stroke End
Ok, vậy là bạn đã biết cách tạo 1 path và gán path đó cho shapeLayer.
Nếu coi việc vẽ 1 path từ đầu đến cuối là 100% công việc, vậy giả sử nếu bạn chỉ muốn vẽ được 40, 50 hay 60% của công việc đó?
-> strokeStart và strokeEnd sinh ra là để giúp bạn làm điều đó.
strokeStart: Chạy trong khoảng 0 đến 1, giá trị default là 0, tức là sẽ vẽ từ đầu.
strokeEnd: Chạy trong khoảng 0 đến 1, giá trị default là 1, tức là sẽ vẽ cho đến cuối cùng.
Khi bắt đầu công việc vẽ thì sẽ bắt đầu từ strokeStart đến strokeEnd.
Vẫn đoạn code vẽ hình vuông ở trên, giả sử bạn chỉ muốn vẽ từ đầu cho đến 70% công việc thì thêm dòng code sau:
shapeLayer.strokeEnd = 0.7
Còn nếu bạn muốn vẽ từ 30% công việc đến 100% công việc thì chỉ cần set strokeStart:
shapeLayer.strokeStart = 0.3
Bạn có thể kết hợp cả strokeStart và strokeEnd tùy ý.
Ngoài ra, strokeStart và strokeEnd còn có thể để dùng để tạo animation. Phần animation này sẽ nói ở bài viết sau.
Fill Color
fillColor là thuộc tính dùng để thay đổi màu cho phần bên trong của 1 hình khối.
Ở phần 1, mình đã có vẽ 1 hình tròn có đường line màu đỏ và phần bên trong không màu. Giờ thì hãy set màu cho phần bên trong hình tròn bằng cách thêm dòng code:
Kể cả đối với các hình không kín thì việc set fillColor vẫn cứ là OK nhé :
Line
Line Width
Nghe cái tên thôi là đã biết attribute này để làm gì rồi 🙂 Đó là set width cho path dùng để vẽ đường thẳng, hình khối.
Thêm dòng code sau vào đoạn code vẽ hình tròn:
shapeLayer.lineWidth = 20.0
Kết quả thu được:
Line Cap
thuộc tính lineCap sẽ quyết định xem điểm cuối của 1 open path được vẽ như thế nào: butt, round hay square.
Hình dưới đây lần lượt là hình của shapeLayer sau khi set lineCap thành butt, round và square:
Để set thuộc tính lineCap cho shapeLayer thì thêm dòng code:
shapeLayer.lineCap = .round hoặc .butt hoặc .square
.round
.square
Line Dash Pattern
Thuộc tính lineDashPattern giúp bạn vẽ những đường thẳng đứt đoạn chứ không phải là 1 đường thẳng liền mạch.
lineDashPattern là 1 mảng các số định nghĩa độ dài nét liền và nét đứt bạn muốn vẽ.
Giả sử bạn muốn vẽ 1 đường thẳng đứt đọan như sau: Cứ vẽ được 1 nét dài 5 thì sẽ đứt đoạn 1 khoảng 15 thì set lineDashPattern như sau:
shapeLayer.lineDashPattern = [5, 15]
Hoặc nếu bạn muốn vẽ 1 nét dài 5 rồi đến 1 nét đứt 10, rồi vẽ 1 nét dài 15 và 1 nét đứt dài 20:
shapeLayer.lineDashPattern = [5, 10, 15, 20]
Cấu trúc lineDashPattern bạn cung cấp sẽ được lặp lại cho đến khi vẽ xong hình.
Line Dash Phase
"Line dash phase specifies how far into the dash pattern the line starts." – Apple
Có thể hiểu đơn giản là lineDashPhase là 1 thuộc tính sẽ cho bạn dịch điểm bắt đầu vẽ đi 1 khoảng bạn muốn. Độ dài của path sẽ không thay đổi.
Đây thực sự là một câu hỏi không dễ để trả lời…
Một cách thông thường, trong suy nghĩ của hầu hết lập trình viên sẽ là: chỗ nào giống nhau, gọi lại nhiều lần thì nên gom thành common class, static cho dễ gọi.
Ok, mọi thứ đều ổn, nhưng nếu ko để ý & quá tay một chút thì nó đã vi phạm nghiêm trọng đến design ứng dụng – nó phá vỡ ranh giới của các element,module hoặc class
Tại sao lại như vậy?
Bản chất Lập trình OOP là mô phỏng thực tế cuộc sống vào chương trình bằng ngôn ngữ lập trình. Thay vì dùng văn tả cảnh, thì lập trình viên tả lại cuộc sống bằng ngôn ngữ lập trình trên một framework nào đấy. Ở ngoài cuộc sống có gì, thì chương trình cũng có cái đó, chúng ta có Sinh viên, có Tài khoản ngân hàng, có Ô tô…etc.
Nhưng nếu bạn để ý, trong cuộc sống Util class, Helper class, Common class… ko tồn tại.
Ai đó tạo lên cuộc sống hẳn phải là một lập trình viên cực kỳ vĩ đại.
Phân ranh giới. (Boundaries)
Software architect là một nghệ thuật để tạo ra các ranh giới, nhằm phân tách các element, class, module..etc
Cùng xem xét ví dụ sau:
DisplayHelper – static function
ZxingDecoder cần lấy ra orientation device, hàm lấy orientation dùng ở rất nhiều nơi nên tạo sẵn một class DisplayHelper để dùng. Hàm get orientation cần context nên parameter của ZxingDecoder là context
Display – interface
Mình tạo ra 1 interface là Display để có thể get orientation của device, parameter đầu vào của ZxingDecoder là display.
Điểm khác biệt lớn nhất là ở cách viết 2 – ZxingDecoder đã tách khỏi(1 phần tách khỏi) framework android – khi nó xóa đi sự hiện diện của context, tức là mình đang cố gắng phân rõ ranh giới của ZxingDecoder khỏi android framework
Nói một cách khác mình đang cố gắng decouple ZxingDecoder khỏi android framework
ZxingDecoder là 1 bộ decode barcode – nó ko phụ thuộc vào framwork, việc decode này mang ý nghĩa – class mình viết có thể chạy được ở bất cứ đâu, không chỉ là trên android framework
Ngoài ra, nếu sử dụng DisplayHelper – khi có càng nhiều nơi, càng nhiều class, layer, module gọi hàm get orientation, hoặc một function của DisplayHelper thì các bạn có thể tượng tượng rằng DisplayHelper như một sợi xích đâm xuyên tất cả các layer, module, class, và buộc chặt các thành phần này lại với nhau và hoàn toàn không thể tách rời. Nói cách khác, khi đó ko thể tạo ra các ranh giới Boundaries cho bất cứ thành phần nào dùng chung DisplayHelper. Tất cả đều phẳng, và dính chặt vào android framework thông qua context của DisplayHelper
Dễ code(Create) – khác với việc dễ maintain(Update).
Phân ranh giới. (Boundaries) – là target cho hầu hết các task refactor bạn phải làm
Làm thế nào để phân ranh giới? à, cái ý mình ko dám nói 😀 (ở bài viết này)
Trong lập trình ứng dụng, sẽ có những lúc bạn phải làm việc với các đường thẳng, các hình khối.
Đôi khi chúng quá đơn giản để phải cần cắt ra các image, đôi khi các ứng dụng đòi hỏi các animation, dẫn đến phức tạp thì không thể dùng image được.
CAShapeLayer được dùng để làm việc với các đường thẳng, hình khối 1 cách đơn giản.
Contents:
How to draw a line
Draw shape with shape layer
How to draw a line?
Học cách vẽ 1 đường thẳng là điều đầu tiên phải học trước khi học vẽ 1 hình khối.
Để vẽ 1 đường thẳng thì cần tối thiểu 2 điểm: điểm đầu và điểm cuối.
UIView có func draw() dùng để vẽ các line:
move(to: ): move đến 1 điểm cụ thể để bắt đầu vẽ.
addLine(to: ): vẽ 1 đường thẳng đến 1 điểm cụ thể.
Kết quả thu được:
Để vẽ 1 đường cong, thì apple cung cấp 2 phương thức addCurve() addQuadCurve():
Với func addCurve() thì sẽ vẽ được 1 đường cong với 2 điểm control point là 2 điểm cong.
Với func addQuadCurve() thì sẽ chỉ có 1 điểm control point để tạo điểm cong.
Kết quả thu được:
Draw shape with shape layer:
Việc sử dụng draw() để vẽ có 1 nhược điểm lớn là nó sẽ bị gọi ngay khi khởi tạo view, và không thể lưu lại. Vậy trong trường hợp bạn muốn vẽ vào 1 thời điểm khác chứ không phải từ đầu?
Ta sẽ sử dụng 1 cách phổ biến và đơn giản hơn, là vẽ bằng cách sử dụng CAShapeLayer.
ShapeLayer là gì?
Nhìn vào hình trên có thể thấy 1 view sẽ chứa 1 root layer kiẻu CALayer và root layer đó sẽ chứa nhiều sublayer.
CAShapeLayer là 1 class kế thừa từ CALayer.
Ta dùng CAShapeLayer để tạo ra các custom layer, sau đó sẽ add đề lên root layer của 1 view như là 1 sublayer. Do đó, ta có thể tạo các custom layer tại bất kì thời điểm nào ta muốn và có thể lưu lại.
Thứ tự hiển thị trên view khi add các sublayer
Vẽ các hình khối:
Giờ thì sẽ bắt đầu vẽ các hình khối trên 1 sublayer và add đè lên layer của view.
1 vài hình đơn giản như hình vuông, hình tam giác,… đơn thuần là các nét thẳng thì có thể vẽ đơn giản bằng cách ghép các đường thẳng:
Vẽ hình vuông:
func createSquare() {
let shapeLayer = CAShapeLayer()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0.0, y: 0.0))
path.addLine(to: CGPoint(x: 100.0, y: 0.0))
path.addLine(to: CGPoint(x: 100.0, y: 100.0))
path.addLine(to: CGPoint(x: 0.0, y: 100.0))
// path.close() // creating a line segment between the first point and current point
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.yellow.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
layer.addSublayer(shapeLayer)
}
Tạo ra 1 shapeLayer.
Tạo ra 1 path, 1 path là sự ghép lại của nhiều đường thẳng.
Sau khi vẽ xong path, gán path cho shapeLayer.
Add đè shapeLayer vừa tạo lên root layer của view.
Đưa đoạn code trên vào hàm init của UIView và chạy thử:
Kết quả thu được:
Hàm close() ở trên được dùng để tạo ra 1 đường thẳng nối điểm hiện tại và điểm đầu tiên khi bắt đầu vẽ.
-> Uncomment hàm close() ở trên và run sẽ thu được kết quả:
Ngoài ra cũng có thể dùng 1 hàm addLine() thay cho hàm close() để nối 2 điểm.
Hình bầu dục:
Để vẽ hình bầu dục, sử dụng hàm UIBezierPath(ovalIn: CGRect):
Hệ thống đánh giá Performance bằng phương pháp Bell Curve là hệ thống mà trong đó cấp quản lý bắt buộc phải đánh giá nhân viên của mình qua việc xếp hạng performance theo thứ tự từ cao xuống thấp. Thông qua hệ thống này, công ty cố gắng phân tách được những người có performance tốt nhất, bình thường, và tệ nhất, từ đó tập trung nuôi dưỡng, phát triển những người giỏi nhất và thay thế những người tệ nhất. Sự phân tách này dựa trên cơ sở so sánh tương đối về performance của những người làm các hoạt động tương đương nhau, rồi xếp hạng họ theo sự so sánh về performance đó.
Phương pháp Bell Curve dựa trên các mô hình xác xuất thống kê. Nó giả định rằng năng lực của nhân viên trong 1 công ty sẽ được phân bố thành các nhóm với tỉ lệ như sau:
High Performers – Top 20%
Average Performers – 70% ở giữa
Non-Performers hay là Below Average Performers – 10% cuối.
Vì thế, bằng việc chấm điểm performance (rating) cho từng nhân viên rồi ranking họ, sau đó lấy ra 20% đầu thì sẽ định danh được đó chính là “High Performers” đem lại phần lớn hiệu quả trong công ty. 70% tiếp theo sẽ là “Average Performers” còn 10% cuối chính là những Below Average Performers đang không đem lại hiệu quả tốt. (Cách gọi mỗi nhóm và tỉ lệ chia được đưa ra dựa trên phân tích của lãnh đạo từng công ty về trình độ nhân sự, đặc thù công việc, bản chất lĩnh vực hoạt động của công ty. Ví dụ như ở Fsoft quy định là: A+: top 5% performance cao nhất, A: từ 20% đến 30%, B: Từ 55% đến 65% ở giữa, C: 9%, D: 1% có performance thấp nhất)
Phương pháp Bell Curve ép buộc cấp quản lý phải ranking nhân viên của mình như thế này cũng có những điểm có lợi và bất lợi riêng của nó.
Lợi ích của hệ thống đánh giá Performance bằng Bell Curve
1. Bắt Manager phải đối mặt trực diện với vấn đề và giải quyết kịp thời
Các nhà quản lý có xu hướng đánh giá performance một cách nhẹ nhàng (thang điểm nhẹ tay) để không tạo ra bất kỳ căng thẳng nào, tránh các cuộc hội thoại thường không thoải mái có thể làm mất lòng nhân viên. Điều này có thể dẫn đến tình trạng “lạm phát điểm số” trong công ty. (Điểm số của tất cả các nhân viên đều cao nhưng không nói lên thực trạng của vấn đề).
Phương pháp Bell Curve không để điều đó xảy ra. Nó buộc manager phải xếp hạng nhân viên của mình, từ đó bộc lộ ra những nhóm người được đánh giá là Below Average Performers cho dù điểm rating có cao hay thấp. Vì thế, phương pháp này buộc những người quản lý phải có những cuộc trò chuyện hứa hẹn là khó khăn với Below Average Performers, và (có lẽ là) các vấn đề nghiêm trọng sẽ được giải quyết ngay sau đó.
2. Có thể là một công cụ tạo động lực tăng năng suất
Phương pháp Bell Curve giúp chính công ty lẫn từng nhân viên có động lực để trưởng thành hơn nhờ vào áp lực cạnh tranh rất lớn mà nó đem đến. Nhớ rằng: trong phương pháp này, để được đánh giá là Top Performers, việc duy nhất một nhân viên có thể làm là thể hiện tốt hơn người khác.
3. Nhanh chóng xác định được top performers
Xác định được những người làm việc hiệu quả hàng đầu (top performers) và thưởng cho họ xứng đáng luôn là một việc hết sức quan trọng đối với 1 công ty. Việc này giúp công ty giữ chân được những người tài năng nhất. Bell Curve là phương pháp dễ dàng và nhanh chóng để giúp manager xác định được đâu thực sự là top performers trong công ty.
Điểm bất lợi của Bell Curve: Quá cứng nhắc để phù hợp với tất cả – Great performers are still average
Managers buộc phải xếp hạng đưa nhân viên và phân bố vào các nhóm trong Bell Curve, nghĩa là một số người được xếp loại là ‘bad’, hoặc ‘Below Average’ trong khi performance của họ không đến nỗi quá tệ. Họ có thể vẫn là những người thể hiện tuyệt vời và đáp ứng đúng kỳ vọng trong vai trò của họ, nhưng bị đẩy vào nhóm “Below Average Performers” đơn giản vì khi so sánh với đồng nghiệp của mình, họ không thể hiện vượt trội hơn được.
Điều này có nghĩa là trong 1 nhóm gồm toàn người giỏi, thì người ít giỏi nhất sẽ vẫn bị xếp loại “Below Average Performers” trong khi nếu được xếp trong một môi trường khác, với cùng performance đó lại có thể được đánh giá là “xuất sắc”. Việc này có thể là yếu tố làm giảm tinh thần làm việc của nhân viên trong 1 số trường hợp.
Kết mở:
Không có mô hình nào là hoàn hảo. Quan trọng là cách vận dụng sao cho khéo léo và đúng bản chất, và vẫn có phương án backup trong một số trường hợp nhé 😛 .
Bài viết được đăng trên Medium tuần trước, đọc thấy có nhiều thông tin hữu ích nên dịch để giới thiệu với mọi người trong thời kỳ mô hình WFH – Work From Hearts đang lên ngôi <3
COVID-19
đã buộc các công ty trên toàn thế giới phải ban hành – hoặc tạo ra – các phương
thức làm việc từ xa. Các công ty như Box, Amazon, Airbnb, Facebook, Google và
Microsoft đều đã và đang thử nghiệm với nhân viên của họ một số biến thể của
hình thức làm việc từ xa.
Giờ đây, các tổ chức truyền thống ở các lĩnh vực như bất động sản, kế toán và chính quyền địa phương cũng bắt đầu với hình thức này. Các cuộc gọi nhóm video call trên Zoom, như trong tấm ảnh dưới đây, được thực hiện bởi một số nhóm làm việc vừa bắt đầu làm quen với hình thức làm việc từ xa, đang tràn ngập trên Twitter và LinkedIn cũng như Facebook.
Tuy nhiên, cũng giống như các công việc có giá trị, luôn có nhiều mức độ thành thạo và tinh tế khác nhau tương ứng với từng quy mô của nó. Nhiều nhân viên lần đầu làm việc từ xa coi đó đơn giản là tải xuống Zoom, Slack và truy cập vào email, rồi coi đó là cách giải quyết trọn vẹn cho hình thức làm việc từ xa. Nhưng chiếc áo không làm nên thầy tu – còn rất nhiều điều quan trọng khác mà chúng ta cần quan tâm khi bắt đầu với hình thức làm việc từ xa.
Công ty Automattic
Nói đến đẳng cấp cao nhất trong hình thức làm việc từ xa, có rất ít công ty có thể làm điều đó tốt hơn Automattic – công ty đứng sau WordPress – nơi sở hữu khoảng 35% số trang web trên mạng Internet.
Automattic – tại thời điểm viết bài – có 1.170 nhân viên nằm rải rác trên 75 quốc gia, nói 93 ngôn ngữ. Tự hào có mức định giá công ty là 3 tỷ đô la Mỹ, đã thực hiện một số thương vụ đáng chú ý như mua lại WooCommerce và nền tảng blog, Tumblr.
Công ty này không có văn phòng, với các nhân viên hợp tác làm việc qua phương thức duy nhất là làm việc online.
Matt Mullenweg
Người sáng lập của Automattic, Matt Mullenweg (vì tên anh ta có 2 chữ “t”, nên tên công ty này cũng được nhân đôi số chữ “t” theo) gần đây đã xuất hiện trên kênh podcast Making Sense nổi tiếng của Sam Harris để nói về cái mà anh ta gọi là 5 cấp độ của các nhóm phân tán (anh ta thích dùng từ “phân tán” thay vì “từ xa” vì cụm từ “từ xa” kia ngụ ý rằng vẫn còn một nơi làm việc tập trung.)
Những ý kiến của Mullenweg thật đáng khích lệ (ít nhất là đối với tôi), nó tương tự như những gì tôi đã nói với nhiều khách hàng và đối tác của mình – “Các công cụ chỉ tốt tương đương với cách bạn sử dụng chúng”. Trên thực tế, lạm dụng các công cụ thực sự có thể khiến chúng ta giảm năng suất.
Bên dưới là cách giải thích và diễn giải của tôi về những ý kiến của Mullenweg về 5 cấp độ của làm việc từ xa.
Năm cấp độ của làm việc từ xa
Cấp 1: Hành động không có chủ ý
Các công ty không có chủ ý hỗ trợ làm việc từ xa, tuy nhiên các nhân viên vẫn có thể tiếp tục một phần công việc nếu họ ở nhà trong một ngày.
Họ có quyền truy cập vào điện thoại thông minh và email của mình. Và có lẽ họ cũng sẽ tham gia vào một vài cuộc họp.
Nhưng rồi họ sẽ tạm gác hầu hết mọi việc cho đến khi họ trở lại văn phòng, rồi lại trở thành một cái bóng của chính bản thân mình trong văn phòng như thường lệ.
Cấp 1 là nơi mà phần lớn
các tổ chức & nhân viên đang làm việc trước khi có sự bùng phát của đại dịch COVID19.
Cấp độ 2: Tái tạo lại văn phòng làm việc dưới hình thức trực tuyến
Đây là nơi hầu hết các tổ chức hiện đang cư trú trong dịch COVID19 – đặc biệt là những tổ chức truyền thống.
Đây là nơi nhân viên của bạn có quyền truy cập vào phần mềm họp trực tuyến (ví dụ: Zoom), phần mềm nhắn tin (ví dụ: Slack) và email, nhưng thay vì thiết kế lại công việc để tận dụng các phương tiện mới này, cuối cùng các nhóm sẽ xây dựng lại cách họ làm việc trong văn phòng như trước đây nhưng dưới hình thức trực tuyến.
Điều này làm trầm trọng thêm nhiều thói quen xấu đã xâm nhập vào văn phòng hiện đại và ngăn chặn khả năng suy nghĩ của những người lao động tri thức – đó là khi bạn phải nhìn vào các cuộc gọi video 10 người trong khi chỉ cần hai người là đủ, hay hơn 60 lần gián đoạn mỗi ngày qua Slack và các cuộc gọi điện thoại, kiểm tra thông báo và trả lời email hơn 70 lần một ngày trong suốt cả ngày, hoặc là khả năng phản hồi nhanh được mong đợi đối với tất cả các nhân viên, khiến họ bị buộc dây với chiếc máy tính như một phản xạ có điều kiện.
Mullenweg coi việc thiếu thiết kế lại công việc xung quanh các phương tiện mới tương tự như sự thiếu hiệu quả trong nhiều dự án chuyển đổi kỹ thuật số trị giá hàng triệu đô la, mà ở đó các quy trình thủ công bị lỗi và dư thừa được tạo ra trong những năm 1980 lại tiếp tục được số hóa một cách hiệu quả – nhưng chờ đã – chúng vẫn là các quy trình bị lỗi, và chúng bị dư thừa cơ mà?
Ở cấp độ 2, mọi người vẫn được yêu cầu có mặt trực tuyến từ 9h sáng tới 5h chiều và nếu bạn ở cấp độ 2, bạn vẫn còn một chặng đường dài để đi.
Cấp độ 3: Thích nghi với các công cụ mới
Ở cấp độ 3, các tổ chức bắt đầu thích nghi và tận dụng lợi
thế của các công cụ mới. Mullenweg nói đến các
tài liệu được chia sẻ (chẳng hạn như qua Google Doc), được nhìn thấy bởi tất cả
mọi người và cập nhật liên tục theo thời gian thực trong suốt cuộc thảo luận, từ
đó có được sự hiểu biết chung về những gì được thảo luận cũng như được quyết định,
giúp loại bỏ nguy cơ bị lãng phí thời gian khi có sự hiểu nhầm về nội dung muốn
truyền đạt.
Ở giai đoạn này, các công ty cũng bắt đầu đầu tư vào các
thiết bị tốt hơn cho nhân viên của mình, chẳng hạn như công cụ hỗ trợ cho các cuộc gọi video như webcam và micro chống ồn.
Giao tiếp bằng văn bản hiệu quả trở nên cấp thiết khi các công ty muốn ủng hộ cho hình thức làm việc từ xa. Khó chịu với việc phải tham gia vào những cuộc gọi bất chợt, và ưu tiên cho giao tiếp bất đồng bộ (sẽ được nói đến chi tiết hơn ở phần tiếp theo), hầu hết các giao tiếp tại Automattic dựa trên nền tảng văn bản, và do đó phối hợp ăn khớp một cách nhịp nhàng và đúng lúc trở thành yếu tố quyết định.
Trên thực tế, Mullenweg chia sẻ rằng hầu hết các công việc tuyển dụng ở công ty ông được thực
hiện qua văn bản thay vì các cuộc gọi điện thoại hoặc cuộc gọi video cho ứng viên.
Khi nhắc đến các cuộc họp:
Chỉ tổ chức một cuộc họp nếu
điều đó là hoàn toàn cần thiết và không thể đạt được kết quả
tương tự bằng hình thức cuộc trò chuyện nhanh, cuộc
gọi điện thoại, email, văn bản hoặc tin nhắn.
Giới hạn thời gian mặc định cho cuộc họp là 15 phút và chỉ kéo dài thời gian nếu thực sự cần thiết
(cuộc họp càng ngắn, bạn sẽ càng phải trao đổi ngắn gọn và rõ ràng, hạn chế được thời gian nói chuyện lan
man vô nghĩa).
Đặt một lịch trình họp cụ thể và kết quả mong muốn
trước khi thực hiện cuộc họp.
Chỉ mời những người bắt buộc phải có (trừ khi là những quyết định lớn cần nhiều người tham gia, còn thường thì hai người sẽ đưa ra
được quyết định trong khi ba người lại hiếm khi).
Đồng ý về các bước triển khai tiếp theo, phân bổ người
có trách nhiệm và đặt ngày đến hạn (điều này đặc biệt quan trọng để tránh các
cuộc họp boomerang – họp
xong như chưa họp).
Không bao giờ, sử dụng một
cuộc họp chỉ đơn giản là để truyền đạt thông tin – đó là những gì mà email hoặc tin nhắn
được thiết kế để giải quyết. Nhiều người thực sự đang thấy rằng hầu hết các cuộc họp này thực tế có thể giải quyết qua email.
Cấp độ 4: Giao tiếp bất đồng bộ
“Tôi sẽ đụng tới nó cho tới lúc thích hợp.” – Đây là bản chất của giao tiếp bất đồng bộ.
Thực
tế là hầu hết mọi thứ không
đòi hỏi phải phản ứng lại ngay lập
tức. Đối với hầu hết mọi thứ chẳng hạn như thư điện tử hay tin nhắn tức
thời nên thực hiện công việc của
nó là truyền đạt thông tin, còn người nhận sẽ trả lời khi đến thời điểm phù hợp với họ.
Nếu
một cái gì đó thực sự khẩn cấp, thì phương thức giao tiếp sẽ phản ánh điều đó.
Nhấc điện thoại, hoặc vỗ vai người
muốn
gọi – nhưng chỉ khi đó là
việc thực sự khẩn cấp.
Bên
cạnh lợi ích rõ ràng và to lớn của việc dành cho những người lao động tri thức có thời gian suy nghĩ,
sáng tạo và đi vào trạng thái dòng chảy
(một trạng thái tâm lý theo đó chúng ta có năng suất cao hơn năm lần
theo McKinsey), giao tiếp bất đồng
bộ còn
khiến mọi người đưa ra quyết định tốt hơn.
Như
Robert Greene nói: nếu bạn muốn cắt cảm xúc ra khỏi phương trình, hãy tăng thời
gian phản hồi của bạn. Cho mọi người thời gian để suy nghĩ giữa câu hỏi và câu trả lời, thay vì trở thành nạn nhân của việc
luôn phải thốt ra điều đầu tiên xuất
hiện
trong đầu ở các cuộc họp hay khi bị vỗ
vai, sẽ
mang lại lợi ích chung cho tổ chức về lâu
dài.
Để
tránh việc chuyền qua chuyền lại cũng như mất nhiều thời gian trao đổi, hãy chắc chắn rằng các thông điệp bất đồng bộ luôn đảm bảo:
Cung cấp đầy đủ
và
chi tiết bối cảnh của tình huống, cũng
như cung cấp các hành động cần thiết một cách rõ ràng đi kèm với các kết quả
mong muốn.
Cung cấp thông tin về ngày cần hoàn thành.
Cung cấp cách thức liên hệ trong trường hợp người nhận cần thêm thông tin hoặc không thể đáp ứng yêu cầu của bạn.
Ví dụ:
“Chào An,
Kèm theo là tài liệu hợp nhất cho công ty mới được tách ra từ công ty hiện tại của chúng tôi.
Vui lòng ký vào tài liệu khi được yêu cầu và gửi lại cho tôi trước 4 giờ chiều thứ Sáu tuần này.
Nếu bạn có bất kỳ mối quan tâm nào, hãy gọi cho tôi vào số 555 1983.”
Các
công ty thực sự thực hiện giao tiếp bất đồng bộ đã vượt qua cuộc cách mạng công nghiệp, và không còn giới thiệu sự
hiện diện của mình thông qua
năng suất, hoặc sản lượng hàng giờ,
như các
nhà máy hiện tại đang làm.
Mullenweg
chỉ ra rằng các đội phân tán toàn cầu,
những người làm việc bất đồng bộ
và thành thạo với cách làm việc kiểu chạy tiếp sức, có thể hoàn thành lượng việc cao gấp ba lần so với một nhóm làm việc với các thành
viên ở trong văn phòng từ 9 giờ sáng đến 5
giờ chiều.
Đánh thức con cú đêm trong bạn
Một
yếu tố chưa
được bàn tới trong các cuộc trao đổi đó là nhịp sinh học.
Khoa
học cho thấy các mô hình
ngủ phổ
biến của chúng ta – các kiểu nhịp
sinh học – được lập trình từ khi sinh. Mọi người hoặc là cú đêm hoặc sẽ là chim sâu buổi sớm.
Nhà
vật lý thiên văn Sabrina Stierwalt đã viết cho Science American rằng:
Sở thích của chúng ta đối với cái này hay cái khác được mã hóa trong các gen được gọi là
gen “đồng hồ” hoặc gen “chu kỳ” qua đó điều chỉnh
nhịp sinh học của chúng ta, và liên
quan trực tiếp đến huyết
áp, sự trao đổi chất, nhiệt độ
cơ thể cũng như mức độ
hormone.
Một
số nghiên cứu đã phát hiện ra rằng khoảng 30 đến 40% dân số là những con cú
đêm, điều đó có nghĩa là ngày làm việc 9h đến 5h
hiện đại đang phá hoại những nỗ lực sáng tạo và trí tuệ của gần một nửa lực lượng
lao động.
Stierwalt
nói rằng ngày làm việc thường bắt đầu từ 7 đến 9 giờ sáng. Tuy nhiên, những con
cú đêm có thể gặp hiện tượng “lệch múi giờ do tác động xã hội” nếu họ
thức dậy sớm thế này – nghĩa là, họ có thể cảm thấy hiện tượng tương tự như hiện
tượng chúng ta đã gặp sau một chuyến bay
qua đêm. Những người dậy sớm thường ít gặp phải tình trạng lệch
múi giờ này, điều
này giúp họ có lợi thế hơn những con cú
đêm.
Các
nghiên cứu cho thấy rằng trong khi những người dậy sớm tỉnh táo hơn vào buổi
sáng, thì cú đêm cho thấy sự tập trung mạnh mẽ hơn và sự chú ý kéo dài hơn 10
giờ sau khi thức dậy so với đồng loại “chim sâu buổi
sớm” của họ.
Các
công ty bất đồng bộ cung cấp cho cú đêm sự linh hoạt trong việc bắt đầu ngày làm việc mới muộn hơn, miễn là phải đảm bảo có những khoảng
thời gian mà họ và đồng nghiệp khác cùng làm việc với nhau.
Và
như Mullenweg chia
sẻ: Công ty Automattic đang ở cấp 4
Cấp 5: Niết Bàn (Nirvana)
Đây
là nơi nhóm phân tán của bạn
hoạt động tốt hơn bất kỳ nhóm làm việc trực tiếp tại chỗ nào từng có thể. Mullenweg đánh đồng mức độ này với việc
nhấn mạnh hơn vào thiết kế môi trường,
trong đó
luôn có sự quan tâm đến văn hóa tổ chức, và
môi trường vật lý mà mọi người đang làm việc.
Nhược điểm
Tất
nhiên, có những ưu và nhược điểm với hầu hết các quyết định.
Có
thể tìm thấy ba nhược điểm hoặc mối lo ngại lớn đối với các đội mới làm việc
từ xa và cách đối phó với chúng dưới
đây:
Liên kết đội và xây dựng
Thay
vì nói với nhân viên của họ ở văn phòng 11 tháng một năm và được nghỉ 4 tuần,
Automattic lật ngược lại kịch bản này:
“Nhân viên có 11 tháng làm việc từ xa một
năm và phải dành thời gian để đi lại tới 4 tuần một năm cho các sự kiện gắn kết
và xây dựng đội nhóm.”
Họ
cũng sử dụng các ứng dụng được xây dựng tùy chỉnh để theo dõi ai đã gặp ai, và
sau đó chỉ định chỗ ngồi, nói tại một bữa tiệc tối, để mọi người ngồi với những
người mà họ chưa gặp trước đó.
Sự thấu hiểu và giao tiếp trong văn phòng
Với tất cả mọi người làm việc trực tuyến, bạn bỏ lỡ các cuộc trò chuyện tán gẫu khi chạm mặt nhau ở quầy ăn/cây lấy nước, hay tình cờ nghe thấy người khác nói về điều mà bạn có thể giúp đỡ, hoặc có ngay nhận thức chung về các hoạt động của nhóm nhờ việc đứng từ xa lắng nghe những cuộc trao đổi.
Để giải quyết sự thiếu hụt này, Automattic sử dụng một plugin WordPress có tên P2, hoạt động như một blog nội bộ và là nơi có một số lượng đáng kể các cuộc trò chuyện và hoạt động được ghi lại.
Sự an toàn
Mullenweg
nói
đến sự an toàn với phương thức bảo mật đầu cuối sử dụng trong hình
thức BYOD – sử
dụng thiết bị cá nhân như máy
tính xách tay và điện thoại thông minh.
Tuy nhiên, thay vì nhấn mạnh hay tập trung quá mức đến sự kiểm soát truy cập, chúng ta cần được bảo vệ để chống lại các hành vi độc hại. Với hơn 70% các vụ hack CNTT sử dụng kỹ thuật Social Engineering – kỹ thuật thao túng hành vi con người để xâm nhập vào bên trong thay vì tập trung khai thác các lỗ hổng bảo mật của máy móc, thiết bị.
“Hiện tại, làm việc tại nhà là một đặc quyền và nó không phải là quyền của nhiều người, vì vậy chúng ta hãy chú ý hơn về cách thức mình làm việc, hãy chứng minh rằng chúng ta có thể làm việc hiệu quả hơn so với làm việc tại văn phòng qua đó chúng ta sẽ được cho phép làm việc từ bất cứ nơi nào chúng ta muốn thường xuyên hơn.”
Extensions – có tác dung đúng như tên gọi của nó, là dùng để mở rộng những class, struct, enum hoặc protocol đã được define.
Nó cho phép bạn mở rộng cả những class, struct mà bạn không có quyền truy cập để sửa source code như Int, String,.. hay UICollectionView, UITableView,…
Bài viết này sẽ nói về extensions trong swift và những cách sử dụng căn bản.
Content
Extension syntax
Initializes
Methods
Conform to protocol, delegate
Extension syntax:
Khai báo extensions với extension keyword:
extension SomeType {
// new functionality to add to SomeType goes here
}
Công dụng của extension:
Add thêm các property, computed property.
Add thêm các methods.
Cung cấp các initializers mới.
Định nghĩa subscripts
Định nghĩa và sử dụng các nested type
Extend 1 class để làm class đó conform các protocol, delegate.
Initialize:
Extension có thể dùng để thêm các initialize mới cho các kiểu dữ liệu đã được define.
Đối với class, ở extension thì chỉ có thể thêm các convenience init mới, không được thêm các init hoặc deinit:
designated init và deinit không được đặt tại extension của 1 class
Đối với struct thì có thể thêm các init, deinit vào extension:
Methods:
Sử dụng extension để thêm các func mới cho 1 existing type:
class Human {
func eat() {
print("Eating")
}
}
extension Human {
func play() {
print("Playing")
}
func sleep() {
print("Sleeping")
}
}
let me = Human()
me.eat()
me.play()
me.sleep()
Đối với value type như struct và enum, nếu func được thêm vào mà làm thay đổi giá trị của property của struct, enum thì func đó phải được define là mutating.
Ở ví dụ dưới đây, vì func changeName thay đổi property name, nên func đó phải đc define là mutating bằng cách thêm mutating keyword vào đằng trước func.
struct Dog {
var name: String
}
extension Dog {
mutating func changeName(newName: String) {
self.name = newName
}
}
Có thể dùng extension để add thêm các func cho những source code mà bạn không được truy cập đến:
Ở đây, bạn add thêm 1 func square để tính gía trị bình phương cho struct Int.
Vì vậy, bất cứ 1 instance nào có kiểu dữ liệu là Int đều có func square mà bạn add thêm.
Conform to protocol, delegate
1 công dụng nữa của extension là extend 1 class để làm cho class đó conform 1 protocol, delegate:
Thông thường, có thể làm 1 class conform protocol bằng cách viết:
class AController: UIViewController, UICollectionViewDataSource. UICollectionViewDelegate {
}
Note: Nên tách ra thành nhiều extension khi extend 1 class để làm class đó conform protocol, delegate để clear và dễ dàng đọc code hơn.
Kết luận:
Dùng extension để làm cho source code dễ đọc và clear hơn.
Dùng extension để add thêm các func cho những class như ViewController, TableView, … hoặc struct như Int, String,… để có thể tái sử dụng, tránh duplicate code.
Ngoài ra, extension còn dùng để add thêm các nested type, các subscrip, cách làm cũng tương tự như khi add thêm method, property.
Có thể tham khảm thêm tại: https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
Nếu 1 entity có access level là open, thì nó có thể được truy cập từ bất cứ nơi nào trong module mà thực thể đó được định nghĩa, và cả từ 1 module khác bên ngoài mà đang import module đó.
Khi truy cập từ module bên ngoài hoặc bên trong thì đều có thể kế thừa, override,… lại entity đó.
> Note: Access level open thường được sử dụng cho các framework, dể bạn có thể dễ dàng kế thừa, override, sử dụng các phương thức của framework đó từ trong project của bạn.
Public access
Nếu 1entity có access level là open, thì nó có thể được truy cập từ bất cứ nơi nào trong module mà thực thể đó được định nghĩa, và cả từ 1 module khác bên ngoài mà đang import module đó.
Level access thấp hơn open bởi khi truy cập từ module bên ngoài thì không cho phép kế thừa, override,… lại entity đó.
Internal access
1 entity có access level là internal thì sẽ chỉ được sử dụng tại bất cứ nơi nào trong module định nghĩa entity đó, nhưng không thể sử dụng bên ngoài module định nghĩa nó.
1 entity nếu nếu không được set access level cụ thể thì mặc định access level là internal.
name và func printName có access level mặc định là internal.
Fileprivate
1 entity có access level là fileprivate thì sẽ hạn chế quyền truy cập của module định nghĩa tới entity đó. Entity đó chỉ cho phép truy cập tới nó từ bên trong swift file định nghĩa nó.
class A có property name kiểu fileprivate, property age là kiểu internal.
class B không thể truy cập được đến property name của A bởi A và B nằm ở 2 file swift khác nhau và property name có access level là fileprivate.
-> property name chỉ có thể truy cập được trong classA.swift file.
Private
Là access level có độ truy cập thấp nhất.
1 entity nếu có access level = private thì sẽ chỉ có thể truy cập được từ class khai báo entity đó, hoặc từ extension của class đó nhưng phải trong cùng 1 swift file.
> Bạn nên dùng private access để giấu các phương thức, các biến cụ thể khỏi bên ngoài khi các phương thức, biến đó chỉ được dùng từ bên trong nơi định nghĩa chúng.
classA.swift
class A {
private var name = ""
}
class C {
let a = A()
func printNameOfA() {
// Không thể truy cập được property name ở đây vì C
không phải class định nghĩa name.
}
}
extension A {
func printName(){
print(name)
}
}
classB.swift
extension A {
func printName() {
// Không thể truy cập được property name ở đây vì extension
của A không được khai báo cùng swift file với property name.
}
}
Subclassing
bạn có thể subclass bất cứ class nào có thể truy cập được trong phạm vi truy cập.
1 subclass thì không thể có access level cao hơn so với super class.
Khi override lại các phương thức, class con có thể override lại với 1 access level mới cao hơn so với access level của phương thức của super class.
Getter/Setter
Getters và setter mặc định nhận access level của các biến, property,… mà chúng thuộc về.
Bạn có thể làm cho setter có access level thấp hơn getter bằng cách cung cấp access level riêng cho setter.
Ở đây. bạn tạo access level private cho property name nên không thể gán 1 giá trị mới cho property name từ bên ngoài class.
Protocols
Các func, properties bên trong protocol sẽ chỉ có thẻ có cùng access level cùng với protocol đó.
Nếu bạn tạo ra 1 protocol mới kế thừa từ 1 protocol đã có, thì protocol mới phải có access level thấp hơn so với protocol đã có.
1 class có thể conform to 1 protocol có access level thấp hơn class đó.
Hi vọng bài viết đã tổng quát lại được những kiến thức hữu ích về access control trong swift.
Application’s life cycle – Vòng đời của 1 chương trình. Đây là 1 phần cơ bản nhưng cực kỳ quan trọng trong việc lập trình một ứng dụng. Tuy có thể coi là 1 kỹ năng trấn phái nhưng không phải ai cũng nắm được rõ và đẩy đủ về vòng đời này (minh chứng là vẫn bị tester bắt nhiều bug về các case abnormal liên quan đến các state của app). Chính vì thế mình viết bài này để có thể giới thiệu 1 cách chi tiết về vòng đời của app và cách sử dụng để tránh những lỗi không đáng có.
Một application sẽ có các trạng thái như sau: – Not running: Là trạng thái application chưa được bắt đầu hoặc đã chạy nhưng bị terminated bởi system. – Inactive: Application đang chạy ở Foreground nhưng không nhận bất cứ sự kiện tương tác nào và cũng không thể xử lý các sự kiện (có thể là bị một vài sự kiện tác động vào trong quá trình chạy, ví dụ như có cuộc gọi đến hay tin nhắn chẳng hạn). 1 app cũng có thể ở trong trạng thái này khi chuyển từ state này sang state khác. – Active: Application đang chạy ở Foreground và đang nhận các sự kiện bình thường. Cách duy nhất để đến trạng thái Active là thông qua Inactive. Ở trạng thái này, khi người dùng tương tác với UI, họ có thể nhìn thấy phản hồi cho những hành động của họ. – Background: Application đang chạy ở background và đang thực thi code. Ở trạng thái này UI của app không được hiển thị nhưng mà nó lại vẫn đang chạy (nếu có đăng ký background task với OS). Hầu hết các app chuyển trạng thái sang suspended thông qua trạng thái này. – Suspended: Application đang chạy ở background nhưng không thể thực thi code. Thường thì sẽ do chính system sẽ tự động đưa app về trạng thái này và lúc đó app vẫn đang trong memory. Trong trường hợp low memory, hệ thống có thể sẽ tự kill app của mình khi app đang ở trạng thái suspended mà không thông báo gì. Lưu ý rằng: Theo chuẩn của Apple thì chỉ hệ thống mới có thể kill app.
Về cơ bản thì 1 application có các trạng thái như trên, và trong app chúng ta cũng có các event tương ứng để được notify khi bắt đầu hay đã vào các trạng thái trên. Các hàm đó được list trong AppDelegate. Các trạng thái chuyển đổi qua lại được gọi là transition giữa các trạng thái. – application:willFinishLaunchingWithOptions —— Method này được gọi sau khi app của chúng ta khởi chạy thành công. Nó là method đầu tiên được chạy từ app delegate. Chúng ta có thể thực thi các đoạn code nếu khởi chạy thành công. – application:didFinishLaunchingWithOptions —— Method này được gọi trước khi window của app được hiển thị. Bạn có thể hoàn thiện giao diện của mình và cung cấp root viewcontroller cho window. – applicationDidBecomeActive —— Method này được gọi để báo cho app của bạn biết khi nó chuyển trạng thái từ In-Active sang Active hoặc hệ thống và user khơi động app hoặc trong trường hợp user bỏ quan các gián đoạn làm app ngay lập tức chuyển sang In-Active (như là có cuộc gọi đến hoặc tin nhắn). Bạn nên dùng method này để chạy lại các tác vụ đang bị dừng (hoặc chưa chạy) khi app bắt đầu chạy lại. – applicationWillResignActive —— Method này được gọi để báo cho app biết rằng nó sắp chuyển từ trạng thái Active sang In-Active . Nó xãy ra khi trường hợp bị gián đoạn (có cuộc gọi tới hoặc SMS) hay là khi user tắt app đi. Bạn nên dùng method này để dừng các task đang chạy hoặc vô hiệu hoá timer trong app, hoặc nhiều thứ khác – applicationDidEnterBackground —— Method này được gọi để báo cho app biết nó đang không chạy ở dưới foreground. Bạn có khoảng tầm 5 giây để thực thi các task . Trong trường hợp bạn muốn có nhiều thời gian hơn để xử lý, bạn có thể yêu cầu hệ thống cấp cho thời gian thực thi bằng cách gọi hàm beginBackgroundTask(expirationHandler:) . Nếu như method của bạn không được thực thi và trả về trước thời gian hết hạn thì app sẽ bị hệ thống chấm dứt và xoá khỏi bộ nhớ. – applicationWillEnterForeground —— Method này được gọi như là 1 phần trong việc chuyển trạng thái từ Background sang Active. Bạn nên dùng method này để hoàn thành các thay đổi đối với app trước khi nó xuống Background. applicationDidBecomeActive sẽ được gọi ngay khi method này đã hoàn thành việc chuyển trạng thái của app từ In-Active sang Active. – applicationWillTerminate —— Method này được gọi khi app của bạn sắp bị hệ thống khai tử khỏi bộ nhớ. Bạn nên dùng method này để thực thi các tác vụ dọn dẹp. Bạn có tầm khoảng 5 giây để thực thi tác vụ. Nếu hàm của bạn không trả về trước thời gian hết hạn, hệ thống sẽ tự động khai tử app kèm cã task đang thực thi của bạn khỏi bộ nhớ. Method này cũng được gọi trong trường hợp app đang chạy ở dưới background( không bị suspended) nhưng hệ thống lại cần phải huỷ nó vì vài lí do gì đó. Bạn không nên đợi applicationWillTerminate được gọi rồi mới lưu lại data. Trong 1 vài trường hợp hi hữu, applicationWillTerminate sẽ không được gọi trước khi app bị khai tử (Vd như trong trường hợp máy của bạn reboot lại thì method này sẽ không được gọi).
Đó là tất cả về iOS Application’s life cycle. Cảm ơn mọi người đã theo dõi bài viết. Hi vọng bài viết này có thể giúp ích cho các bạn. Mọi ý kiến đóng góp các bạn vui lòng comment ở bên dưới để mình có thể hoàn thiện hơn ở các bài viết sắp tới.
Đôi khi phát triển ứng dụng chúng ta sẽ phải lưu lại những dữ liệu nhạy cảm như thông tin người dùng, thẻ ngân hàng … Vậy lúc đó chúng ta sẽ phải cần đến Keychain để lưu các dữ liệu đó. Vậy hôm nay mình sẽ giới thiệu với các bạn phương pháp lưu dữ liệu sử dụng Keychain.
Tại sao không dùng các phương pháp lưu dữ liệu khác?
Có thể các bạn đã biết, những thông tin nhạy cảm của người dùng như: thông tin tài khoản(username, pass, token …), thông tin thẻ ngân hàng … nếu để lộ ra ngoài sẽ làm ảnh hưởng rất lớn tời người dùng. Vì vậy nếu chúng ta sử dụng các cách lưu dữ liệu thông thường không có tính bảo mật như: File, Property List(UserDefaults), CoreData, Realm … thì người ta hoàn toàn có thể lấy được nội dung của ứng dụng một cách đơn giản. Về cơ bản các kiểu lưu dữ liệu kia đều sử dụng 1 file để lưu trữ data ở đó mà không sử dụng bất kỳ phương pháp mã hóa nào, vì vậy nó quá dễ dàng để các tin tặc có thể lấy được những dữ liệu bạn lưu trong các file đó.
Sử dụng Keychain Services API
Để lưu trữ những dữ liệu nhạy cảm cần tính bảo mật Apple đã cung cấp cho chúng ta Security Services. Keychain services API sẽ giúp chúng ta lưu trữ các dữ liệu này ở trong một encrypted database được gọi là Keychain.
Để lưu Password vào keychain mình sẽ làm như sau:
func save(_ password: String, for account: String) {
let password = password.data(using: String.Encoding.utf8)!
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: password]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { return print("save error")}
}
Keychain service cung cấp một số kiểu để xác định nơi lưu trữ phù hợp. kSecClassGenericPassword là một trong số đó, keychain services sẽ hiểu items này là password và cần được mã hóa. Ngoài ra còn có các kiểu khác các bạn tìm hiểu ở ĐÂY.
SecItemAdd(_:_:) để lưu dữ liệu vào keychain
Để lấy lại dữ liệu chúng ta đã lưu chúng ta làm như sau:
func getPassword(for account: String) -> String? {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: kCFBooleanTrue]
var retrivedData: AnyObject? = nil
let _ = SecItemCopyMatching(query as CFDictionary, &retrivedData)
guard let data = retrivedData as? Data else {return nil}
return String(data: data, encoding: String.Encoding.utf8)
}
Để lấy dữ liệu ra chúng ta sử dụng SecItemCopyMatching() truyền vào đó query mà mình muốn.
Đối với các bạn lần đầu làm việc với Keychain API thì nó cũng khá lằng nhằng. Nhưng hiện nay đã có khá nhiều thư viện ngon hỗ trợ việc code của các bạn trở nên dễ dàng hơn như: Keychain-swift, Keychain Access
Mã hóa thông tin
Ngoài Password ra đôi khi chúng ta cũng cần phải mã hóa một số loại thông tin nhạy cảm khác. Việc mã hóa bằng tay khá phức tạp, vì vậy để hỗ trợ cho việc mã hóa mình thường dùng CryptoSwift.
Tổng kết
Mình hi vọng bài viết có thể giúp các bạn khi các bạn gặp phải các bài toán cần phải lưu những dữ liệu nhạy cảm. Khi đó nhớ chọn Keychain nhé. Chúc các bạn thành công.
Bài viết tiếp theo mình sẽ chia sẻ thêm một tính năng rất hay của keychain đó là Keychain Sharing
Cách lưu dữ liệu sử dụng file Property List khá phổ biến trong lập trình ứng dụng iOS, nó thường được dùng để lưu các dữ liệu nhỏ và không cần tính bảo mật cao. Bài viết này mình sẽ chia sẻ với các bạn một số cách để thao tác với Property List.
Info.plist
Tất cả các project khi bạn mới tạo XCode sẽ tự tạo ra một file Info.plist, nó làm nhiệm vụ lưu lại các thông tin dự án của bạn. Từ project name, version cho đến các setting cũng như mô tả API của Apple.
Các thông tin trong file Info.plist sẽ được các API của Apple truy cập vào để lấy thông tin hiển thị lên ứng dụng. (VD: Hiển thị message cho pop up xin quyền truy cập Camera …) cũng như việc xác nhận thông tin của Apple khi bạn Submit ứng dụng lên App Store Connect. Vì vậy file này chỉ nên lưu những thông tin cài đặt của dự án. Nếu bạn muốn lưu dữ liệu dạng này ta có thể tạo một file mới và lưu vào đó.
Tạo file Property List mới
Bước 1: Chuột phải vào thư mục bạn muốn lưu file -> New File…
Bước 2: Đánh ô tìm kiếm phía trên với keyword “Property List” -> Next -> Đặt tên file
Vậy là bạn đã tạo thành công file Property List. Giờ bạn có thể mở file và điền thông tin mình muốn lưu vào file đó.
Tuy nhiên nếu sử dụng XCode thì chúng ta chỉ có thể lưu được các dữ liệu như:
String
Number
Bool
Data
Date
Array
Dictionary
Chúng ta cũng có thể lưu kiểu custom object vào file Property List nhưng phải sử dụng code để encode dữ liệu cần lưu sang Data rồi khi sử dụng thì chúng ta decode nó về dữ liệu ban đầu.
Tạo file Property List bằng code
Chúng ta có thể tạo file Property List một cách đơn giản hơn bằng cách như sau:
func save(key: String, value: Any) {
let myData: NSDictionary = [key: value]
let fileManager = FileManager.default
// Đường dẫn lưu file và tên file của bạn
let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo3.plist")
// Viết vào file nếu file không tồn tại nó sẽ tự tạo file mới.
myData.write(to: path, atomically: true)
print(path)// in ra console để thấy đường dẫn lưu file
}
Truy cập vào file Property List bằng code
Lấy data từ file Property list
func getMyPlist(key: String) -> Any? {
// Lấy ra đường dẫn vào file Property List của bạn
let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
// Sử dụng NSDIctionary để lấy nội dung từ URL
guard let myDict = NSDictionary(contentsOf: path) else { return nil }
print(path)
// Trả về giá trị theo key trong Dictionary
return myDict[key]
}
Lưu dữ liệu vào file Property List
func saveMyPlist(key: String, value: Any) {
let fileManager = FileManager.default
// Tạo đường dẫn file
let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
guard let myDict = NSMutableDictionary(contentsOf: path) else {
// nếu file chưa tồn tại chúng ta sẽ ghi vào file với bộ key/value đầu tiên.
let myData: NSDictionary = [key: value]
myData.write(to: path, atomically: true)
return
}
// nếu file đã tồn tại chúng ta sẽ update dữ liệu
myDict[key] = value
myDict.write(to: path, atomically: true)
print(path)
}
Xóa một key trong Property List
func removeMyPlist(key: String) {
let fileManager = FileManager.default
// Tạo đường dẫn file
let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
guard let myDict = NSMutableDictionary(contentsOf: path) else { return }
// nếu file đã tồn tại chúng ta sẽ update dữ liệu
myDict.removeObject(forKey: key)
myDict.write(to: path, atomically: true)
}
Để tương tác dễ hơn với plist bắt buộc chúng ta phải tạo ra các func để quản lý các file đó. Nhưng nếu các bạn không muốn làm những thứ lằng nhằng đó thì chúng ta có thể chuyển qua dùng một file Property List có sẵn mà Apple đã cung cấp đó là sử dụng UserDefault
UserDefault
UserDefault là một singleton class nhằm mục đích giúp các nhà phát triển có thể dễ dàng lưu dữ liệu vào file Plist hơn. Mặc định khi bạn sử dụng UserDefault nó sẽ tạo ra một file Plist trong dự án. Từ đó ta có thể thêm, sửa, xóa các key nằm trong file Plist đó dựa trên các hàm mà UserDefault cung cấp.
UserDefault tương tác trên Property List nên nó cũng phải tuân thủ theo quy tắc của file đó. Vì vậy thông thường chung ta chỉ có thể gán được các giá trị mà file Plist cho phép.
Để lưu/lấy giá trị bằng UserDefault ta làm như sau:
let udf = UserDefaults.standard//Tạo instance
udf.set("11", forKey: "1")// gán giá trị 11 cho key "1"
udf.synchronize()//nó block thread đang gọi cho đến khi giá trị được gán hoàn tất
print(udf.value(forKey: "1"))// lấy dữ liệu của key 1 và in ra console
Như vậy với UserDefault chúng ta có thể làm việc với file Plist một cách đơn giản. Tuy nhiên, đôi khi chúng ta cũng phải lưu lại kiểu dữ liệu mà Plist không hỗ trợ. Lúc này chúng ta cần phải encode nó về Data để có thể lưu được dạng này.
Lưu ý
Chúng ta không nên lưu những dữ liệu quá nặng trong Plist, vì file này sẽ tiêu tốn bộ nhớ RAM khi bạn chạy ứng dụng.
Lưu custom object
Tạo mới class
class Employee: NSObject, NSCoding {
let name: String
let dob: String
// hàm khởi tạo
init(name: String, dob: String) {
self.name = name
self.dob = dob
}
// init với NSCoder bước này để decode
required convenience init(coder aDecoder: NSCoder) {
let name = aDecoder.decodeObject(forKey: "name") as! String
let dob = aDecoder.decodeObject(forKey: "dob") as! String
self.init(name: name, dob: dob)
}
// hàm này để encode
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(dob, forKey: "dob")
}
}
Lưu Employee vào UserDefault sử dụng NSKeyedArchiver
let employee = Employee(name: "Dino", dob: "19/09/1999")
let udf = UserDefaults.standard
do {
let encodeData = try NSKeyedArchiver.archivedData(withRootObject: employee, requiringSecureCoding: false)
udf.set(encodeData, forKey: "emp")
udf.synchronize()
} catch {
print(error)
}
Lấy ra Employee từ UserDefault
let decode = udf.data(forKey: "emp")
let decodeEmp = NSKeyedUnarchiver.unarchiveObject(with: decode!) as? Employee
Tổng kêt
Mình hi vọng bài viết này có thể giúp các bạn sử dụng Property List tốt hơn. Chúc các bạn thành công.