Blog

  • Basic CAShapeLayer iOS (P2)

    Basic CAShapeLayer iOS (P2)

    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:
    shapeLayer.fillColor = UIColor.yellow.cgColor

    Code vẽ hình tròn:

    private func createCircle() {
        let shapeLayer = CAShapeLayer()
            
        shapeLayer.lineWidth = 2.0
        shapeLayer.fillColor = UIColor.yellow.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        let openCirclePath = UIBezierPath(arcCenter: CGPoint(x: 60.0, y: 60.0),
                                          radius: 60.0,
                                          startAngle: 0.0,
                                          endAngle: CGFloat.pi * 2,
                                          clockwise: true)
            
        shapeLayer.path = openCirclePath.cgPath
        layer.addSublayer(shapeLayer)
    }
    

    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

    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.
    • lineDashPattern có default value = 0.
    let circleShapeLayer1 = ...
    circleShapeLayer1.lineWidth = 2.0
    circleShapeLayer1.lineDashPattern = [47.12]
    
    let circleShapeLayer2 = ...
    circleShapeLayer2.lineWidth = 2.0
    circleShapeLayer2.lineDashPattern = [47.12]
    circleShapeLayer2.lineDasePhase = 23.56
    
    let circleShapeLayer3 = ...
    circleShapeLayer3.lineWidth = 2.0
    circleShapeLayer3.lineDashPattern = [47.12]
    circleShapeLayer3.lineDasePhase = -23.56

    Ở bài viết tiếp theo, mình sẽ nói về animation của CAShapeLayer.

    refer: https://www.calayer.com/core-animation/2016/05/22/cashapelayer-in-depth.html

  • Tại sao nên hạn chế sử dụng Singleton, static function(util class, Helper class)?

    Tại sao nên hạn chế sử dụng Singleton, static function(util class, Helper class)?

    Đâ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

    Alt

    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

    Alt

    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)

  • Basic CAShapeLayer iOS (P1)

    Basic CAShapeLayer iOS (P1)

    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():

    1. Với func addCurve() thì sẽ vẽ được 1 đường cong với 2 điểm control point là 2 điểm cong.
    2. 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):

    private func createOval() {
        let shapeLayer = CAShapeLayer()
            
        shapeLayer.lineWidth = 2.0
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = UIColor.red.cgColor
        let openCirclePath = UIBezierPath(ovalIn: self.bounds)
            
        shapeLayer.path = openCirclePath.cgPath        
        layer.addSublayer(shapeLayer)
    }

    Bạn cung cấp 1 frame để vẽ hình bầu dục trong đó, ở đây mình sẽ dùng view.bounds. Kết quả thu được:

    Vẽ hình tròn:

    Để vẽ 1 hình tròn thì trước hết cần phải biết các góc trong 1 hình tròn:

    Sau đó chỉ cần dùng hàm được cung cấp sẵn:

    private func createCircle() {
        let shapeLayer = CAShapeLayer()
            
        shapeLayer.lineWidth = 2.0
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = UIColor.red.cgColor
        let openCirclePath = UIBezierPath(arcCenter: CGPoint(x: 60.0, y: 60.0),
                                          radius: 60.0,
                                          startAngle: 0.0,
                                          endAngle: CGFloat.pi * 2,
                                          clockwise: true)
            
        shapeLayer.path = openCirclePath.cgPath
        layer.addSublayer(shapeLayer)
    }
    • arcCenter: tâm hình tròn
    • radius: Bán kính
    • startAngle, endAngle: Điểm bắt đầu và điểm kết thúc.
    • clockWise: có vẽ theo chiều kim đồng hồ không.

    Tùy vào bạn muốn vẽ 1 góc bao nhiêu độ mà thay 1 radius thích hợp.
    Kết quả thu được:

    Bài viết này đã tóm tắt về cách vẽ hình cơ bản.
    Ở bài viết sau về shapeLayer, sẽ nói về ShapeLayer Attribute và ShapeLayer Animation.

  • Phương pháp Bell Curve trong đánh giá Performance của nhân viên. Hay là: “tại sao em perform ko đến nỗi tệ mà vẫn bị đánh giá D” ?!

    Phương pháp Bell Curve trong đánh giá Performance của nhân viên. Hay là: “tại sao em perform ko đến nỗi tệ mà vẫn bị đánh giá D” ?!

    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é 😛 .

    Tham khảo và lược dịch từ:

    http://www.cavinhr.com/relevance-of-bell-curve-method-of-performance-appraisal/

    https://blog.cake.hr/performance-appraisals-should-you-grade-your-employees-on-a-bell-curve-%F0%9F%94%94/

  • 5 cấp độ của làm việc từ xa – và tại sao bạn có thể đang ở cấp độ 2

    5 cấp độ của làm việc từ xa – và tại sao bạn có thể đang ở cấp độ 2

    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.”

  • [Swift] Extensions

    [Swift] Extensions

    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 {
    
    }

    Hoặc sử dụng extension:

    class AController: UIViewController {
    
    }
    
    // MARK: - UICollectionViewDataSource
    extension AController: UICollectionViewDataSource {
    
    }
    
    // MARK: - UICollectionViewDelegate
    extension AController: 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

  • Access Control in Swift

    Access Control in Swift

    Content

    1. Giới thiệu về Acess Control
    2. Các loại access control
    3. Subclassing
    4. Getter/Setter
    5. Protocols

    Các loại access control

    Swift cung cấp 5 loại access control:

    Open access:

    • Là loại access có độ truy cập cao nhất.
    • 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.

  • The Application’s Life Cycle

    The Application’s Life Cycle

    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ó. 

    iOS App life cycle - Brian - Medium


    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.

    Thanks all from with love <3
    KhanhVD1.

  • iOS/Swift: Lưu dữ liệu bằng Keychain

    iOS/Swift: Lưu dữ liệu bằng Keychain

    Đô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

  • iOS/Swift: Lưu dữ liệu sử dụng Property List

    iOS/Swift: Lưu dữ liệu sử dụng Property List

    Lời mở đầu

    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.